2.1 判断对象已经死亡的两种方式
2.1.1 引用计数法
概念:每一个对象都有一个计数器,当被一个引用指向的时候就把计数器加一。当一个引用失效就减一。所以说当一个对象的计数器为零的时候表示对象不在使用了。
问题:当对象出现循环引用的问题时,无法解决。
2.1.2 可达性分析算法
概念:通过一系列的“GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索走过的路线成为引用链,当一个对象到GC Roots没有任何引用链相连,则证明此对象是不可用的。
GC Roots:
- 虚拟机栈中的引用对象(栈帧中的本地变量表)
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中JNI引用的对象。
2.1.3 再谈引用
几种引用的类型:
强引用:就是我们常见的创建对象的方式,只要强引用还存在,垃圾回收器永远不会回收掉引用的对象。
软引用:有用但非必须的对象。在系统将要发生内存溢出异常的时候,将会把这些对象列进回收范围进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存 溢出异常。SoftReference类来实现软引用。
弱引用:当垃圾收集器发生时,无论内存是否充足还是不充足直接就回收掉。WeakReference类实现弱引用。
虚引用:无法通过虚引用来区的一个对象的实例。唯一的目的就是这个对象回收的时候会受到一个系统通知。
2.1.4 生存还是死亡
- 在可达性分析中不可达的对象,也并不一定是非死不可的。
- 要真正宣布一个对象死亡,至少要经历两次标记过程。
- 如果对象在进行可达性分析之后发现没有与GC Roots相链的引用链,进行第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者方法已经被执行过一次,虚拟机将这两种情况都视为不需要执行。
- 如果这个对象有必要执行finalize方法,对象会被放到一个叫做F-Queue的队列中。
- 将由虚拟机创建一个低优先级的Finalizer线程去执它(不保证这个方法一定会运行完)。
- finalize()方法是对象逃脱死亡的最后一次机会。
- 接下来GC将对F-Queue中的对象进行第二次标记,如果对象要在finalize中拯救了自己(重新与引用链上的的任何一个对象奖励关联就可),那么第二次标记就会将它移除即将回收的集合,如果还是没有逃脱基本上就会被回收了。
2.2 垃圾回收算法
2.2.1 标记—清除算法
概念:从名字可以看出,这个算法主要是两个流程,标记—清除。首先会标记出所有需要回收的对象,在标记完成后进行统一回收。
存在两个问题:
- 效率问题:标记和清除两个过程效率都特别低。
- 空间问题:空间碎片太多。
2.2.2 复制算法
概念:该算法应用在新生代,新生代内存分为三块:Eden、Form、To区。比例为8:1:1。
存在的问题:当对象存活率较高的情况下,需要进行比较多的复制操作,效率会变低。
复制算法过程:
-
对象的内存分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目
前存放对象的那一块),少数情况会直接分配到老生代(空间分配担保)。
-
当新生代的 Eden Space 和 From Space 空间不足时就会发生一次 GC,进行 GC 后,Eden
Space 和 From Space 区的存活对象会被挪到 To Space,然后将 Eden Space 和 From
Space 进行清理。
-
如果 To Space 无法足够存储某个对象,则将这个对象存储到老生代。
-
在进行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反复循环。‘
-
当对象在 Survivor 区躲过一次 GC 后,其年龄就会+1。默认情况下年龄到达 15 的对象会被
移到老生代中。
2.2.3 标记整理算法
概念:标记过程仍然与标记——清除算法一样。但是后续不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动。然后直接清理掉端边界以外的内存。
主要应用在老年代。
2.2.4 分代收集算法
目前市面上大部分虚拟机都是分代收集算法。
2.3 垃圾收集器
2.3.1 Serial收集器
概念:最基本的收集器,单线程收集器,它在进行垃圾收集的时候,会暂停其他的所有工作线程,知道它结束。对于限定单个 CPU 环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率,因此 Serial垃圾收集器依然是 java 虚拟机运行在 Client 模式下默认的新生代垃圾收集器。(但是目前来说还是没有用到过的,太恐怖了,你的程序运行一个小时的时候会莫名其妙的暂停5分钟)
2.3.2 ParNew 收集器
概念:它是Serial收集器的多线程版本。
它是许多运行在Server模式下的虚拟机中首选的新生代收集器,除了有一个很重要的原因是,只有他能与CMS收集器配合工作。
2.3.3 Parallel Scavenge收集器
概念:它是一个新生代收集器,它也是使用复制算法的收集器。并行的多线程收集器。它的目标是达到一个可控制的吞吐量。吞吐量= 用户代码运行时间 / cpu总运行时间。
2.3.4 Serial Old收集器
概念:是Serial收集器的老年代版本,单线程收集器 ,使用标记整理算法。(主要是给Client模式使用)
2.3.5 Paraller Old收集器
概念:Paraller Old是Paraller Scavenge 收集器的老年代版本,使用多线程和标记整理算法。
2.3.6 CMS收集器
概念:它是一种以回去最短回收停顿时间为目标的收集器。使用标记清除算法,但是比之前面的更加复杂一些,整个步骤分为四步:
- 初始标记
- 并发标记
- 重新标记
- 并发清除
其中初始标记和重新标记这两个步骤仍然需要Stop the World。初始标记仅仅是标记一下GC ROots能直接关联到的对象。,并发标记阶段就是进行GC Roots Tracing 的过程。而重新标记阶段是为了修正并发标记期间因用户 程序继续运作而导致标记产生变动的那一部分对象的标记记录。
2.3.7 G1收集器
特性:
- 并行和并发
- 分代收集
- 空间整合
- 可预测的停顿
运行步骤:
- 初始标记
- 并发标记
- 最终标记
- 筛选回归