什么时候回收
1、应用程序空闲时
2、堆内存不足
对象回收判定
引用计数算法
1、给对象添加一个引用计数器,只要有地方引用它,计数器+1,引用失效-1,当gc触发时候,计数器为0的被回收.
2、object-c就是这种,很难处理循环引用,相互引用的两个对象则无法释放。(需要开发者自己处理)
可达性分析算法
通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链项链时,则证明此对象是不可用的。
GC Roots的对象
1.虚拟机栈(栈帧中的局部变量表)中的引用对象
2.方法区中类静态属性(静态对象)引用的对象
3.方法区中常量(final 修饰的成员对象)引用的对象。
4.本地方法栈中JNI(Native)引用的对象。
对象引用
强引用:垃圾收集器永远不会回收掉被引用的对象,即使出现oom
软引用:在系统将要发生内存溢出(OOM)异常之前,将会把这些对象列进回收
弱引用:它的引用比软引用更弱一些,不管内存够不够用,只要触发gc,就会回收
虚引用:无法通过虚引用来取得一个对象的实例。为一个对象设置虚引用的唯一目的就是能够在这个对象被回收的时候收到一个系统通知
垃圾回收算法
标记 - 清除算法
通过可达性分析法,标记需要回收的对象,接着统一清除(效率低),会造成不连续的内存碎片,当有需要大片内存需要的时候,会提前触发GC.
复制算法
将内存划分二份,每次只用其中一份,当其中一份内存用完时,就把存活的对象,放入未用过的一份,把用过的清除掉,这样比较高效,但每次使用的内存会小一倍,适用存活对象少,垃圾多的情况下.
标记 - 整理算法
标记存活的对象,整理、移动到一端,直接清除另一端垃圾,适合存活对象多,垃圾少的情景.
分代收集算法
根据对象存活周期的不同将内存划分为几块,进而采用不同的回收算法的策略,Java堆分成新生代和老年代.
新生代:
1、对象朝生暮死,存活的对象少,可回收对象多。选用复制算法,只要付出少量存活对象的复制成本就可以完成收集
老年代:
1、对象存活率高,回收的对象很少。选用标记 - 清理算法,或者标记 - 整理算法来进行回收,它与新生代的比例为 2(老年代) : 1(新生代),比例可以调整
工作流程:
1、分配的对象放到Eden区,当Eden区满的时候,触发gc,把Eden区存活的对象copy到Survivor A区,然后清空Eden区和Survivor B区(虽然Survivor B区刚开始是空的)
2、接着分配对象到Eden区,当Eden区满的时候,触发gc,把Eden区和Survivor A区存活的对象copy到Survivor B区,清空Eden区和Survivor A区
3、就这样存活的对象在Survivor A区和Survivor B区来回捣腾,等到对象在这两个区待够n次后,就会被移到老年代区
参考: https://blog.csdn.net/qian520ao/article/details/79024835