JVM详情
- 一、JVM内存划分
- 二、双亲委派模型(重点考察)
- 三、 GC(垃圾回收机制)
- 垃圾的判定算法
- 垃圾回收算法
一、JVM内存划分
-
堆:存放new出来的对象;(成员变量)
-
方法区:存放的是类对象;(静态变量)
-
栈(虚拟机栈, 本地方法栈):存放方法之间的调用关系;(局部变量)
-
程序计数器:存放的是下一个要执行的指令;
注意:变量存放在哪一个区域,和变量类型无关!和变量的形态(局部,成员,静态)有关!
- a 是成员变量,在堆上
- b 是静态变量,在方法区
- c 是成员变量,在堆上
- d 是静态引用对象在方法区,new 出来的对象在堆上
- A 是局部变量,在栈上,new 出来的对象在堆上
二、双亲委派模型(重点考察)
什么是双亲委派模型?
一个类加载器收到类加载请求,首先自己不会加载这个类,而是把这个请求委派给父类加载器完成,每一层都是如此,因此所有加载请求最终都会送到最顶层的加载器中,只有父加载器反馈无法加载这个请求,子类才会尝试去加载;
2.4.2、涉及到的类加载器
-
Bootstrap ClassLoader :负责加载标准库中的类;
-
Extension ClassLoader:负责加载JVM扩展的库的类(标准库中没有,但JVM自己实现出了);
-
Application ClassLoader :负责加载我们自己的项目中的自定义类;
三、 GC(垃圾回收机制)
在学习C语言的过程中,需要通过 malloc 申请内存,最后通过 free 进行释放,这里就容易存在一个问题——忘记free,造成内存泄漏;而GC(垃圾回收)就是一个主流处理方案;
垃圾的判定算法
-
可达性分析(JVM采用)
顾名思义,就可以到达的变量 约定一些特定的变量,成为 “GC roots”, 每隔一段时间,从 GCroots 出发,进行遍历,查询哪些变量是能够被访问到的,能被访问到的变量就称为 “可达”,否则就是 “不可达” -- 栈上的变量; -- 常量池引用的对象; -- 方法区中静态属性引用对象; -- 方法区中常量引用的对象;
-
引用计数
每多一个引用指向该对象,计数器就+1; 每少一个引用指向该对象,计数器就-1; 当计数器的数值为 0 时,就说明这个对象已经没有人能够再使用了,此时就可以进行释放; 但是如果有相互引用的情况,类似于死锁,那这个内存就永远也释放不了
垃圾回收算法
-
标记-清除算法
简单来说,就是标记出垃圾后,直接把对象对应的内存空间进行释放; 但是假如释放了3块空间不连续,那么虽然内存上有3块空间,但是却new不出一个完整的2块空间 引起了内存碎片问题
-
复制算法
这是针对内存碎片问题,引入的办法;具体的,将内存分成两块大小相等的空间,但只使用其中的一块,当需要进行垃圾回收时,就把正在使用的那块空间上还存活的对象复制到另一块上,再将使用过的那块内存全部清空;这样做的好处就是不用再考虑内存碎片问题; 1. 空间利用率相比标记清除法更低了; 2. 但是若一轮GC下来,大部分需要保留,只有极少数要回收,这时候复制的开销就很大了;
-
标记整理算法
标记过程与 “标记-清除算法”一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端连续性的覆盖,然后直接清除掉边界以外的空间;(类似于顺序表的删除元素)
-
分代算法
分代算法综合了上面所说的三种算法,通过区域划分,实现不同区域用不同的垃圾回收策略,从而实现更好的垃圾回收 1. 刚创建出来的对象,进入伊甸区; 2. 若新对象熬过一轮GC,没被回收,就通过复制算法,复制到生存区; 3. 生存区的对象也要经历GC,每熬过一次GC,就会通过复制算法拷贝到另一个生存区(只要这个对象不死亡,就会在两个生存区来回拷贝); 4. 如果一个对象在生存区中,反复坚持了很多轮还没去世,就会被放到老年代(老年代GC的频率会降低); 5. 若对象来到了老年代,也会进行定期的GC,只是频率更低了;老年代采取标记整理的方式来处理垃圾;