java GC
对象存活判定
引用计数算法
给对象一个引用计数器,当有引用指向该对象时,计数器加1。当引用断开时,计数器减1。
如果计数器为0,那么就代表该对象为垃圾,需要对其回收。
缺陷:无法解决循环引用的问题
存在两个失去引用的对象a和b。a中有一个引用成员指向了b,而b中也有一个引用成员指向了a。致使,虽然a和b都是垃圾,但是它们的引用计数器不为0。导致无法被回收。
可达性分析算法
通过一系列的GC Roots 对象作为起始点,从这些节点开始往下搜索,搜索所走过的路径称为Reference(引用链)。当对象到GC Roots没有任何引用链相连时(就是从GC Roots到这个对象不可达),则证明此对象是不可用的。
可以作为GC Roots的对象:
* 虚拟机栈中的引用对象。
* 方法区中类静态属性引用的对象。
* 方法区中常量引用的对象。
* 本地方法栈JNI(Native方法)引用的对象。
引用
Strong Reference:
只要强引用存在,垃圾回收器永远都不会回收掉被引用的对象。
Soft Reference
用来描述还有用,但不必须的对象。在JVM将要发生内存溢出之前,会对这种引用进行二次回收。如果回收后,还是没有足够的内存,才会抛出溢出。
Weak Reference
用来描述非必须的引用,强度比软引用更弱。弱引用只能生存在下一次垃圾收集之前,当垃圾收集器工作时,不管内存是否足够,都会被释放。
Phantom Reference
无法通过一个虚引用取得对象实例。之所以要有虚引用,是因为这个引用被回收时会收到一个系统通知。
垃圾回收算法
标记-清除算法
首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
缺点:
标记和清除效率都不高。
标记清除之后会产生大量不连续的内存碎片,导致以后在分配较大的对象时,因无法找到连续的内存而不得不提前触发另一次垃圾收集工作。
复制算法
把可用内存分为大小相等的两块,每次只使用其中一块。当这一块内存用完了,就将还存活的对象复制到另一块上面,然后在把使用过的内存空间一次释放掉。
优点:
1)每次只对半个区进行垃圾回收
2)不用考虑内存碎片等复杂情况
3)只需移动堆顶指针,按顺序分配内存,运行高效,实现简单。
缺点:内存缩小一半,代价高昂。
普遍用这种算法回收新生代。因为其中的对象98%生命周期非常短暂,所以并不需要按1:1划分内存。
将内存分为较大的Eden和Survivor空间。每次使用其中的Eden和其中一块Survivor。当回收时,将Eden和Survivor中存活的对象一次性的复制到另一块Survivor中,最后清理掉用过的Eden和Survivor。
HotSpot默认Eden和Survivor大小为:8:1。
分配担保机制:
当另一块Survivor不够存放上一次收集下来的存活对象时,这些对象通过分配担保机制直接进入老年代。
新生代:其中大多数对象都是“朝生夕死”。
标记-整理算法
复制法应当对象存活率比较低的情况。而对于老年代就十分不适合。
和标记清除法一样先标记要清除的对象,然后不是直接清除,而是让所有存活的对象都向一端移动,从而清理内存。
分代收集算法
根据对象的存活周期将对象划分为几块,一般是把堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适合的算法。新生代使用复制算法,老年代中使用标记整理算法或标记清除算法。