《深入理解Java虚拟机》:垃圾收集器与内存分配策略

《深入理解Java虚拟机》:对象创建、布局和访问全过程
如何判断对象已死?引用计数法:每个对象维护一个引用计数器 , 当一个对象被引用 , 计数器加1 , 当引用失效时 , 计数器减1 , 任何时刻计数器为0的对象是不可能被引用的 。
引用计数法的最大问题是 , 它很难解决对象之间的相互循环引用问题 。 如下代码 , objA和objB已经没有外部引用了 , 但是互相引用 , 在引用计数法判断对象是否已死的情况下 , objA和objB计数器一直都是1:
public class ReferenceCounttingGc{ private Object instance = null ; private static final int _1MB = 1024 * 1024 ; public static void main(String[] args) {ReferenceCounttingGc objA = new ReferenceCounttingGc();ReferenceCounttingGc objB = new ReferenceCounttingGc();objA.instance = objB;objB.instance = objA;objA = null;objB = null;System.gc(); }}可达性分析算法:这种算法解决了引用计数法无法解决的相互引用问题 。 它最重要的一个概念就是根对象 , 即GC Roots 。
当一个对象到GC Roots没有任何引用链相连时 , 则证明此对象不可用 。 如图所示 , object5、object6、object7虽然互相关联 , 但是到GC Roots不可达 , 所以它们会被判定为可回收的对象 。
《深入理解Java虚拟机》:垃圾收集器与内存分配策略文章插图
哪些可以称之为根对象呢 , 主要是指那些肯定不会被垃圾回收掉的对象 , 比如栈、方法区、本地方法栈中的对象:
虚拟机栈中引用对象;
方法区中类静态属性引用的对象;
方法区中常量引用的对象;
本地方法栈中JNI引用的对象 。
回收方法区:垃圾回收主要在java堆内存 , 其实永久代也有垃圾回收 , 只是回收“性价比”一般比较低 。 收集主要回收两部分内容:废弃常量和无用的类 。
废弃常量回收与堆对象回收非常类似 , 假如一个字符串“abc”在常量池 , 但是当前系统 , 没有任何一个String对象是叫做“abc”的 , 如果这时发生垃圾回收 , 这个“abc”常量就会被清理出常量池 。
判断“无用的类” , 一般需要同时满足以下3个条件:

  1. 该类所有的实例都已经被回收 , 也就是java堆内存不存在该类的任何实例;
  2. 加载该类的ClassLoader已经被回收;
  3. 该类对应的java.lang.Class对象没有在任何地方被引用 , 无法在任何地方通过反射访问该类的方法 。
垃圾收集算法标记-清除:算法分两阶段 , 首先标记出需要回收的对象 , 然后统一回收标记过的对象 。
它的主要缺点 , 一个是效率问题 , 标记-清楚两个过程效率都不高;二是内存碎片过大 , 程序运行期间需要分配大大对象时 , 无法找到连续的内存空间不得不提前触发领另一次垃圾收集动作 。
《深入理解Java虚拟机》:垃圾收集器与内存分配策略文章插图
复制算法:它将内存分为大小相等的两块 , 每次只用其中一块 , 当这一块内存用完 , 就将还存活的对象复制到另外一块上面 , 然后再把使用的内存空间一次清理掉 。 hotspot的新生代就是采用这种复制算法 , 在from和to区来回复制 。
【《深入理解Java虚拟机》:垃圾收集器与内存分配策略】它的优点是内存空间连续;缺点是要浪费一块空内存间 , 且存活对象过多的情况下 , 复制效率低下 。
标记-整理算法:复制算法在对象存活率比较高的情况下复制效率低下 , 而且还浪费50%的内存空间 , 于是有人提出了基于标记-清除算法的优化的算法——标记-整理 。
标记-整理算法的清除不是直接对可回收对象进行清除 , 而让所有存活对象都向一端移动 , 然后清理掉另一端的内存 。
《深入理解Java虚拟机》:垃圾收集器与内存分配策略文章插图
分代收集算法:基于新生代和老年代对象生命周期不同的特点 , 采用不同收集算法的一种算法 。
在新生代中 , 每次垃圾回收都有大批量的对象死去 , 只有少了存活 , 可以选用复制算法;而老年代对象存活率高、必须使用“标记-清除”或者“标记-整理”算法 。
垃圾收集器垃圾收集器是垃圾收集算法的具体实现 。 以下是所有收集器的总图 , 横线上半部分是支持新生代垃圾回收的收集器 , 横线下半部分是支持老年代垃圾回收的收集器 , 如果两个收集器之间有连线 , 说明它们可以搭配使用:
《深入理解Java虚拟机》:垃圾收集器与内存分配策略文章插图
Serial垃圾收集器(单线程、复制算法):单线程的含义是它只会使用一个CPU或者一条收集线程去完成垃圾回收工作 , 更重要的是在它进行垃圾收集时 , 必须暂停掉其他所有的工作线程(用户线程) 。