目录
此篇学习笔记总结包括了3个方面的知识点:
1、JVM内存区域划分
2、JVM类加载机制
3、JVM垃圾回收机制
后面就将重点来介绍这3个方面的知识收获
JVM内存区域划分
JVM内存区域划分可以分为四部分:堆、栈、方法区(元数据区)、程序计数器
1、堆(线程共享)
堆的作用:程序中创建的所有对象都在保存在堆中,即new 出来的所有对象(全局变量、成员方法)
堆是JVM 中最大的内存区域
2、方法区(线程共享)
方法区的作用:用来存储被虚拟机加载的类信息、常量、静态变量、编译器编译后的代码等数据,即类加载之后的类对象(静态变量、静态方法)
3、栈(线程私有)
栈的作用:维护方法之间的调用关系(局部变量)
栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中
4、程序计数器(线程私有)
程序计数器的作用:用来记录当前线程执行的行号的。 程序计数器是一块比较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。
举例:
public class test {
public int a; // 全局变量(也可叫做test这个类的成员变量)
public static int b; // 静态变量
public void method() {
int c = 0; // 局部变量
System.out.println("这是test这个类的一个普通成员方法");
}
public static void run() {
System.out.println("这是一个静态方法!"); // 静态方法不需要借助实例化对象
}
public static void main(String[] args) {
test test1 = new test(); // test1 与 test2 是引用类型,也是局部变量
test test2 = new test(); // new test是一个实例化对象
}
}
堆中存放的有:全局变量a、成员方法method、new test()
栈中存放的有:局部变量c、test1、test2
方法区中存放的有:静态变量b、静态方法run
JVM类加载机制
类加载可以理解为把.class文件从文件 (硬盘) 被加载到内存 (元数据区) 的过程
加载
在寻找.class文件的过程中,需要使用到双亲委派模型来寻找,双亲委派模型在下面文章讲解
验证
准备
解析
具体解析过程如下:
初始化
双亲委派模型
双亲委派模型的作用就是在进行加载过程中,找.class文件这个过程。
在JVM加载的过程中需要使用到类加载器,JVM里面内置了三个类加载器:
它们三者关系如下:
双亲委派模型的原理:当我们需要去加载一个具体类时,不会去扫描自己的目录,而是优先去扫描父类加载器,直到扫描到最后一个父类加载器。最后一个父类加载器若扫描到具体类就进行后续加载步骤,若没有扫描到就交给自己孩子来处理。
举例:若我们想要去加载"java.lang.String"这个类时,扫描过程如下:
双亲委派模型也是可以打破~~
你自己实现的类加载器,可以继续遵守~~也可以不遵守~~(Tomcat里针对webapp的类加载器就没遵守)
JVM垃圾回收机制(GC)
在JVM 内存区域中,
1、栈随着线程一起销毁,当方法调用完毕,方法的局部变量自然随着出栈操作就销毁了
2、程序计数器就是一个单纯存地址的整数,也是随着线程一起销毁
3、方法区(元数据区)存放的类对象,很少会卸载
4、堆(new 出来的对象)存放的是全局变量,因此GC回收的目标主要是堆,GC是以 对象 为单位进行释放
JVM垃圾回收机制主要分为2个阶段:
1、寻找:谁是垃圾
2、释放:把垃圾对象的内存给释放掉
1、寻找
一个对象判断是否是垃圾,只需要判断其是否有引用指向对象即可,若无引用指向对象,则该对象可以被视认为垃圾。
在垃圾回收机制中,对于如何判断一个对象是否有引用指向主要有2种方法:
1.引用计数法 (Python 、 PHP编程语言采用的这种方法)
2.可达性分析 (Java 采用的此方法)
引用计数法(Python、PHP采用)
引用计数法就是在对象里面安排一个额外的空间,保存一个整数,来记录指向该对象的引用个数。每个对象都有一个单独的计数器,不是每一个类独有。
若引用个数为0,说明该对象没有引用指向就可以便认为是垃圾
随着引用的增加,计数器就随着增加;引用的销毁,计数器就随着减少
当计数器为0时,则认为该对象没有引用了,可以当做垃圾进行回收
可达性分析(Java采用)
拿二叉树来举例,通过根节点root可以访问到整棵树的任意节点。
可达性分析,总的来说就是从所有的起点出发,看看该对象里又通过引用能访问到哪些对象,顺藤摸瓜把所有可访问到达的对象都遍历一遍(遍历的同时把对象标记成“可达”),剩下的自然就是“不可达的”。
可达性分析克服了引用计数的两个缺点,但也有自己的问题:
1、消耗更多的时间,因此某个对象成了垃圾,也不一定能第一时间发现,因为在扫描过程中是需要消耗时间的
2、在进行可达性分析的时候,要顺藤摸瓜,一旦在这个过程中,当前代码中的对象的引用关系发生了变化,就十分麻烦(于是,为了更准确的完成这个顺藤摸瓜的过程,就需要让其他业务线程暂停工作,此时便引来STW问题)
2、释放
标记清除
复制算法
复制算法的缺点:复制算法虽然解决了内存碎片问题,但也造成了如下问题:
1、内存利用率比较低
2、如果当前大部分对象都是需要保留的,只有少部分垃圾,此时复制的层本比较高
标记整理
分代回收