最近看完了《深入理解Java虚拟机》的自动内存管理章节,觉得有必要稍微总结一下。实际上我觉得这本书里面有很多地方都只是讲了个大概,就比如这一章节中对7种垃圾回收器的介绍就仅仅是介绍而已,实现的细节都没有讲到。当然了,这本身也因为GC是一个复杂的体系,这么薄薄的一本书也不可能讲的完。
但是看书呢,总有个问题,就是看了就忘,所以为了防止自己忘记,就做了这么一个大纲,然后记上笔记,等到以后复习的时候,就按照这个大纲回忆。
食用方法:
- 阅读《深入理解Java虚拟机》
-
过段时间开始回忆,点击这里查看答案
特别注意
- 《深入理解Java虚拟机》这本书的GC部分本身已经是总结了,所以这个算是总结的总结
- 没有《深入理解Java虚拟机》中的4种垃圾回收算法和7种垃圾收集器的总结,因为他们本身内容就很少,而且CMS和G1收集器讲的也不清楚
- 随后加了个dalvik和hotspot对比的总结,具体dalvik的实现推荐大家去看老罗的文章,art的还没来得及看
文字版
可能xmind这个软件需要钱,那就提供一个文字版吧
GC ROOTS有哪些
- 虚拟机栈中引用的对象
- 方法区的常量和静态变量引用的对象
- 本地方法引用的对象
四种引用及垃圾回收器对它们的回收策略
- 强引用。一般的引用都是强引用,垃圾回收器不会随意回收强引用
- 软引用。当内存不足时,垃圾回收器会把软引用放入可回收范围等待回收
- 弱引用。弱引用的对象只能活到下一次回收
- 虚引用。虚引用不能获取对象,仅仅可以再对象被回收时收到一个系统通知
对象不可达后,就真正死亡了吗?虚拟机是如何执行finalize方法的
对象被标记为不可达后,会被检查是否重写了finalize方法以及有没有执行过finalize方法。如果有重写并且没有执行过的话,虚拟机会把这些对象放到F-Queue中,让finalizer线程去执行这些对象的finalize方法,如果这些对象没有把自己“救活”,那么就会真正标记为“死亡,可以回收”
finalize方法可靠吗
finalize方法不可靠,优先级极低,finalizer线程的优先级也很低,虚拟机只保证会启动这个线程,但是不保证执行结果,建议程序员忘掉这个方法。
保守式GC
什么是不明确的根
我们以调用栈为例,调用栈中存在函数的局部变量和参数的值,这些可以是引用也可以是int之类的基本数据类型。在栈中这些值就是一串数字的排列,所以无法知道哪些是指针哪些不是指针
指针和非指针的识别
- 检查是不是正确对齐的值,在32位机器中,值应该是4的倍数,在64位中,值应该是8的倍数
- 是不是指向堆内
- 是不是指向对象的开头
举例说明貌似指针的非指针
例如有连个地址块上的值都是0x00d0caf0,但是无法分辨到底是这到底是指针还是数值
貌似指针的非指针的对象会被保留
理解“不明确的数据结构”
有的时候,堆里的对象中的数据也会存在无法分清是指针还是非指针的情况
相对应的,就有“半保守式GC”,即在对象上带有类信息,来确保数据结构的明确
优点
- 理解“语言处理程序不依赖于 GC”
缺点
- 理解“识别指针和非指针需要付出成本”
- 理解“错误识别指针会压迫堆”
- 理解“能够使用的GC算法有限”
准确式GC
HotSpot的算法实现
-
oopMap的作用是什么?为什么需要oopMap
为了能够在GC时快速找到什么地方存着什么类型的对象,需要一个映射来存储这些信息。
oopMap就用来保存偏移量和类型的映射 -
什么时候生成oopMap
在类加载完成之后,Hotspot会记录对象内的什么偏移量上是什么类型的数据,然后保存在oopMap里
在JIT过程中,会在一些位置记录下哪些偏移量是什么数据 -
什么是安全点?为什么需要安全点?
在JIT过程中,不可以在每一条指令都记录oopMap,这样会浪费很多的空间,所以需要找到一些不会导致引用关系变化的点来记录,以便节约空间
-
通常那些地方可以作为安全点
方法调用、循环跳转、异常跳转等
-
通常在什么地方可以GC
在安全点的位置可以GC
-
GC时线程是立刻中断吗?
不会立即中断,会执行到安全点
-
线程中断有哪几种方式
一般有两种方式,抢先式中断和主动式中断。
抢先式中断是先强制中断所有的,然后如果没有执行到安全点,就唤醒它让它执行到安全点。
主动式中断是设置一个标志位,然后在安全点轮询这个标志,当GC时把这个标志设置为真,线程轮询到标志为真就中断 -
什么是安全区域?为什么需要安全区域?
有的时候当发生GC时,线程是无法继续执行的,因此无法中断,这时需要用安全区域解决。安全区域是指在一段代码片段之中,引用关系都不会发生变化,在这个区域任何位置开始GC都是安全的。
简述Hotspot的分代GC策略
Hotspot将Java堆分成新生代和老年代,之前还有永久代。新生代中分为eden区和survivor区,survivor区有两种,分别是From区和To区,默认情况下,他们的大小比例为8:1:1。一般来说,新分配的对象放入eden区,特别大的对象直接放入老年代。GC开始时,eden区中存活的对象复制到To区,From区中的对象如果年龄达到阙值就复制到老年代,否则复制到To区,然后清空eden和From,同时将From和To调换,经过一轮GC还存活的对象,将其年龄+1。如果To区不够存放来自eden和From的存活对象时,需要老年代做分配担保,存在老年代中
Hotspot如何动态判定对象年龄
如果一个survivor区中某个年龄对象的总和大于survivor区的一半,那么年龄大于等于这个年龄的直接进入老年代
理解“空间分配担保”
dalvik和Hotspot在GC方面有哪些不同
- 首先相对于Hotspot中CMS系的垃圾回收器,dalvik没有采用分代回收算法,而是采用了标记清除法
- dalvik的GC是半保守式GC,而Hotspot中各个垃圾回收器都是准确式GC。
- dalvik在标记阶段使用的是位图标记法,原因是Zygote Heap写时复制技术
- 位图标记法采用的是两个位图,一个用来记录上一次GC之后还存活的对象,一个用来记录本次GC还存活的对象,然后对他们做异或运算,得到本次GC需要回收的对象