判断对象是否存活,找到成为垃圾的对象。
当一个对象已经不被任何存活的对象继续引用的时候,则判断该对象已经死亡。
概念:
对每一个对象保存一个整形的应用计数器属性。用于记录对象被引用的情况。
当引用计数器的值为0,即表示该对象不可能再被使用,可进行回收。
致命缺点: 无法处理循环引用的情况。
概念:
可达性分析算法是根据GC Roots为起始点,按照从上到下的方式搜索被根对象集合所连接的目标对象是否可达。
GC Roots包括:
⭐ 虚拟机栈中引用的对象
⭐ 本地方法栈中引用的对象
⭐ 方法区中类静态属性引用的对象
字符串常量池中的引用
被同步锁synchronized持有的对象
Java虚拟机内部的引用(基本数据类型对应的Class对象,常驻异常对象,系统类加载器)
使用空间列表分配对象空间
执行过程:
当堆中的触发垃圾回收时,会停止整个程序(STW),然后分别进行标记,清除两个步骤。
标记 :标记所有可达的对象,及所有被引用的对象。将其存放在对象的Header中。
清除 :遍历所有堆中对象,如果没有标记,则清除。
缺点: 效率低;会导致空间碎片化
Eden,Surviver区用的就是这种算法。
使用指针碰撞分配对象空间
执行过程:
将内存空间划分成两个部分。一处作为正在使用的内存,一处作为空闲的内存。在执行垃圾回收时,将可达对象复制到空闲内存区域。两块内存区域互换。如此往复。
优点: 效率高;不会出现碎片问题
缺点: 需要两倍的内存空间;引用关系会发生变化,指针需要改变。
老年代用的就是这种算法。
使用指针碰撞分配对象空间
执行过程:
第一阶段和标记-清除算法一样,从根节点开始标记所有被引用的对象;第二阶段将所有的存活对象压缩到内存的一端,按照顺序排放,之后清理边界外的所有空间。
优点: 不会出现碎片化问题;消除了复制算法中内存减半的代价
缺点: 效率低;引用关系会发生变化,指针需要改变;移动的过程中需要STW
在Java中不同的对象的生命周期是不一样的。因此,不同生命周期的对象采用不同的收集方式,可以提高回收的效率。
年轻代:
特点:区域较小,对象生命周期短,回收频繁
复制算法很好的符合了年轻代对象的特点。
老年代:
特点:区域较大,对象生命周期长,存活率高,回收频率低
Mark阶段的开销与存活对象的数量成正比
Sweep阶段的开销与所管理区域的大小呈正相关
Compact阶段的开销与存活对象的数据成正比
CMS回收器:
CMS回收期是基于Mark-Sweep算法实现的,对于对象回收率很高。而面对碎片问题,CMS采用基于Mark-Compact算法的Serial Old回收期作为补偿措施;当内存回收不佳(碎片导致的Concurrent Mode Failure时),将采用Serial Old执行Full GC以达到对老年代内存的整理。
增量收集算法的基础仍然是标记-清除算法和复制算法。为了解决在垃圾回收时发生的STW。如果此时的STW时间过长,将会严重影响用户体验或者系统的稳定性。因此可以让垃圾收集线程和应用程序线程交替执行。每次垃圾收集线程只收集一小片区域的内存空间,接着切换到应用程序线程。
优点: 避免STW时间过长,减少系统的停顿时间。
缺点: 切换线程和上下文转化会使得垃圾回收成本上升,造成系统吞吐量下降。
为了更好地控制GC产生的停顿时间,将一块大的内存区域分割成多个小块,根据目标的停顿时间,每次合理回收若干个小区间,而不是整个堆空间,从而减少一次GC产生的停顿。
分代算法将按照对象的生命周期长短划分为两个部分,分区算法将堆空间划分成连续的不同小区间region。
每一个region都是独立使用的,独立回收。这种算法的好处是可以控制一次回收多少个小区间。每个region可能会存放Eden、Survivor、Old、Humongous的数据。