如何判断对象是否存活?
1、引用计数算法
理解:给对象中添加一个引用计数器,每当有地方引用时,计数器加1;引用失效时,计数器减1;所以任何时刻计数器为0额对象就是不可能再被使用的。
但是,Java虚拟机没有选用引用计数算法管理内存,主要是因为很难解决对象之间相互循环引用的问题。因为互相引用着对方,导致他们的引用计数都不为0。
2、可达性分析算法
Java、C#等语言,都是通过可达性分析来判定对象是否存活的。
基本思想:通过一系列的“GC Roots” 的对象作为起始点,从这些节点开始向下搜索,搜索走过路径称为引用链,当一个对象到GC Roots 没有任何引用链时(也叫从GC Roots 到这个对象不可达),证明此对象不可用。
可作为GC Roots的对象有:
虚拟机栈中引用的对象;
方法区中类静态应用的对象;
方法区中常量引用的对象;
本地方法栈中JNI(平时说的Native方法)引用的对象;
关于引用?
无论上述哪种算法,判断对象是否存活都与“引用”有关。JDK1.2之后,Java对引用的概念进行了扩充:强引用、软引用、弱引用、虚引用。4种引用强度依次减弱。
强引用:在代码中普遍存在的。;类似“Object obj = new Object();”这类的引用,只要存在,就永不回收;StrongReference类实现强引用。
软引用:描述一些还有用但并非必需的对象。在系统发生内存溢出异常之前,会把这些对象列进回收范围之中进行第二次回收。使用Soft Reference类来实现软引用;
弱引用:也是描述非必需对象的。被软引用的对象只能生存到下一次垃圾收集发生之前。无论内存是否足够,都会回收掉被软引用关联的对象。WeakReference类实现弱引用。
虚引用:也叫幽灵引用或者欢迎引用。是最弱的一种引用关系。对象是否有虚引用的存在,完全不会影响存活时间。唯一目的是在对象被收集器回收时收到一个系统通知。PhantomReference类实现虚引用。
垃圾收集算法 ?
1、标记 - 清除算法
最基础的收集算法。分为“标记”、“清除”两个阶段。首先标记出所有需要回收的对象,在标记完成后统一回收。标记过程就是判断对象是否存活的算法。
主要不足:一是效率问题,两个阶段的效率都不高;其二是空间问题,清除之后会产生大量不连续的内存碎片,导致程序运行过程中需要分配较大的对象时,无法找到足够的连续内存。导致不得不提前触发下一次的垃圾回收动作。
2、复制算法
为了解决效率问题,出现了复制算法。
原理:将可用内存按容量划分为大小相等的两块,每次只是用其中的一块。当这一块内存用完了,就将还存活的对象复制到另一块内存上,再把已使用过的内存空间一次清理掉。
大多数的虚拟机采用这种收集算法回收新生代。
3、标记 - 整理算法
复制收集算法在对象存活率高时就要进行较多的复制操作,效率会变低。并且还会浪费50%的空间。所以老年代一般不能直接用复制算法。
根据老年代的特点,有人提出了 “标记 - 整理” 算法,标记过程与 “标记 - 清除” 算法一样,但是不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
4、分代收集算法
当前商业虚拟机的垃圾收集都采用 “分代手机” 算法,这不是一种新思想,只是根据对象存货周期的不同将内存划分为几块。一般情况下分为新生代和老年代,根据不同代的特点采用适当的收集算法。
新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,所以选用复制算法,代价就是少量存活对象的复制成本;老年代中对象存活率高,必须使用“标记-清除”或者”标记-整理“算法进行回收。
垃圾收集器 ?(暂时跳过总结)
内存分配与回收策略 ?