判断对象存活
引用计数法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象是不可能再被使用的。但解决不了循环引用的问题。
可达性分析法:通过GC Roots作为起始点,从这些节点往下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,即这个对象不可达,则此对象是不可用的。
可作为GC Roots的对象包括:
- 虚拟机栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象
即使在可达性分析算法中不可达的对象也并非“非死不可”,至少要经过两次标记过程:若对象在进行可达性分析后没有与GC Roots相连,就会被第一次标记进行并进行筛选,筛选的条件是此对象是否有必要执行finalize()方法。若对象没有覆盖finalize()方法或已经调用过finalize()方法,视为没必要执行。
若有必要执行finalize()方法,如果对象在finalize()方法中重新与引用链上的任何一个对象建立关联,即完成自救;若没有,则要被回收。
finalize()方法只会被系统调用一次
垃圾收集算法
标记清除算法
首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。标记清楚后会产生大量不连续的内存碎片,当需要分配大对象时,无法找到足够的连续内存而不得不提前触发垃圾收集动作。
复制算法
将内存分为一个Eden区和两块较小的Survivor区,回收时,将Eden和Survivor中还存活的对象一次性复制给另外一个Survivor空间上,最后清理掉Eden与刚才用过的Survivor空间。若另外一块Survivor空间没有足够空间存放上一次新生代收集下来的存活对象,这些对象直接通过分配担保机制进入老年代。
标记整理算法
让所有存活的对象都向另一端移动,然后直接清理掉端边界以外的内存
分代收集算法
堆分为新生代与老年代。在新生代,每次都有大量对象死去,只有少量存活,选用复制算法,只需要复制少量存活对象的复制成本就可以完成收集。在老年代中因为对象存活率高,没有额外空间进行担保,就必须使用标记清除或标记整理算法。
垃圾收集器
Serial收集器
是一个单线程收集器,只会使用一个CPU或一个线程去完成垃圾收集工作,在他进行垃圾收集时,必须暂停其他所有的线程工作,直到他收集结束。新生代采用复制算法,老年代采用标记整理算法。
Serial Old收集器
是Serial收集器的老年代版本,同样是单线程收集器
CMS收集器
是一种获取最短回收停顿时间为目标的收集器,基于标记清除算法实现的,主要优点并发收集、低停顿,缺点为1.对CPU资源非常敏感,导致应用程序变慢;2.无法处理浮动垃圾(指用户线程还在运行,仍产生垃圾,只能等下次GC才回收);3.会有大量空间碎片产生
G1收集器
是一款面向服务端应用的垃圾收集器,将整个Java堆划分为多个大小相等的独立区域,G1会跟踪各个Region里面的垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。
内存分配
- 对象优先在Eden分配
- 大对象直接进入老年代
- 长期存活的对象将进入老年代