深入理解Java虚拟机ch3:垃圾收集器与内存分配策略
主要思考以下3点
-
哪些内存需要回收
-
什么时候回收
-
如何回收
引用计数法
给对象添加引用计数器,每当一个地方引用时,计数器+1,引用失效时,计数器-1
缺陷:循环引用时,双方引用计数不为0,无法回收
可达性分析
通过一系列被称为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索经过的路径称为引用链。当一个对象和GC Roots不连通时,称为该对象不可达。
可作为GC Roots的对象包括下面几种:
-
虚拟机栈(栈帧中的本地变量表)中引用的对象。
-
方法区中类静态属性引用的对象。
-
方法区中常量引用的对象。
-
本地方法栈中JNI(即一般说的Native方法)引用的对象。
引用分类
JDK 1.2前,如果reference类型的数据中存储的值代表另一块内存的起始地址,就称这块内存代表着一个引用 JDK 1.2之后,引用分为强引用,软引用,弱引用,虚引用4种。引用强度依次逐渐减弱。
强引用(Strong Reference):只要存在,垃圾收集器永远不会回收掉被引用的对象。Object obj = new Object()
软引用(Soft Reference):有用非必须对象。在系统将发生内存溢出异常之前,将会把这些对象列进回收范围之中进行二次回收。如果这次回收还没有足够的内存才会抛出异常。
弱引用(Weak Reference):有用非必须对象,但比软引用更弱。被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论内存是否足够都会回收。
虚引用(Phantom Reference):一个对象是否有虚引用存在,不会对生存时间构成影响,也无法通过虚引用来去的一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。
回收条件
要经过2次标记。
第一次标记:可达性分析时,发现没有与GC Roots连接的引用链。并筛选看此对象是否有必要执行finalize()方法。如对象没有覆盖finalize()方法或者finalize()方法以及被虚拟机调用过,虚拟机认为没必要执行。
否则,虚拟机认为有必要执行finalize()方法。那么会放入F-Queue队列中,并在稍后由Finalizer线程执行它,但不承诺执行完毕,防止死循环时F-Queue其他对象永久等待。稍后GC会对F-Queue中对象进行第二次标记。如果此时仍然没有和引用链任何对象关联,那么就会被回收。
垃圾收集算法
标记-清除算法 Mark-Sweep
统一标记,统一回收。标记判断见上文。
缺点:1.效率不高 2.清除会产生大量不连续碎片
复制算法 Copying
内存划分为相等大小的两块,每次用其中一块。当一块快用完的时候,将还存活的对象复制到另一块上。
缺点:内存利用效率太低,只有一半能使用
标记-整理算法 Mark-Compact
标记同标记-清除算法,但后续不是直接对可回收对象清理,而是让所有存活对象向另一端移动,然后清理掉边界外的内存。
分代收集
根据对象存活周期划分内存
新生代:大批对象死去,少量存活,选复制算法
老年代:存活率高,没有额外空间为分配担保,标记-清除算法或标记-整理算法