JVM垃圾回收机制
垃圾回收机制是JVM的重要特点,简单介绍一下JVM垃圾回收
什么算垃圾
JVM垃圾回收时要标记出“垃圾”并清理,那什么算垃圾呢?
垃圾可以理解为内存中没用了的对象,那哪些是没用的对象呢?
有两个方法评价对象是否是垃圾:
引用计数法
为每个对象设置一个引用计数器,每当有人引用这个对象则这个对象的引用计数器加一,当计数器为0时认为这个对象是垃圾,可被回收。
- 优点:实现简单
- 缺点:计数器占空间且无法检测循环引用(你我互相引用,计数器都是1)
可达性分析
定义了一个集合GCRoots根集,根集的元素是对象!对象!对象!
如果对象是GCRoots的对象或者被GCRoots对象直接或间接引用就算可达,像树状图一样从根往下找出所有存活对象。
GCRoots包括:虚拟机栈中引用的对象、方法区中的常量引用的对象、方法区中类静态属性引用的对象、本地方法栈中JNI(Native方法)的引用对象、活跃线程。
- 优点:能解决循环引用的问题
- 缺点:在多线程的情况下,工作线程有可能更新已经访问过的对象的引用
其实被标记到了也不一定会被清理,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize`方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,那对象就死了。
几种引用
- 强引用(Strong Reference):如Object obj = new Object(),这类引用是 Java 程序中最普遍的。只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。
- 软引用(Soft Reference):它用来描述一些可能还有用,但并非必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。JDK1.2 之后提供了SoftReference类来实现软引用。
- 弱引用(Weak Reference):它也是用来描述非必须对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在 JDK1.2 之后,提供了WeakReference类来实现弱引用。
- 虚引用(Phantom Reference):也称为幻引用,最弱的一种引用关系,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被收集器回收时收到一个系统通知。
虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列ReferenceQueue联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
垃圾回收算法
知道什么是垃圾之后,我们来看怎么清理垃圾
标记清理
标记出存活的对象,回收死亡对象的内存。
-
优点是:清理后不用移动存活的对象
-
缺点是:会留下很多不连续的内存碎片,需要另外维护一个列表来记录空闲内存的地址和大小
标记整理
标记出存活的对象,整理移动存活对象到内存的一端,清理存活边界外的内存。标记整理一般依赖于句柄和句柄表。
- 优点:整理后没有内存碎片,空闲内存的地址可知
- 缺点:GC暂停时间长,需要更新对象地址
复制
把内存分成两片一样大的区域(from,to是相对而言的),每次回收时就标记并复制存活对象到to内存,再把from内存回收。
- 优点:也可以避免内存碎片
- 缺点:可用的内存减半了
分代回收
分代回收将堆内存分成了新生代、老年代和永久代(永久代JDK1.8后在元空间)。
新生代被分为几个区域:伊甸园(Eden)和两个存活区(Survivor0/Survi