谈一谈JVM垃圾回收

前言

​ 若是要问Java与其余编程语言最大的不一样是什么,我第一个想到的必定就是Java所运行的JVM所自带的自动垃圾回收机制,如下是我学习JVM垃圾回收机制整理的笔记,但愿能对读者有一些帮助。
<!-- more -->java

哪些内存须要回收?what?

​ 如何判断对象已死?有两种算法算法

引用计数算法

​ 给对象添加一个计数器,每当有一个地方引用它时,计数器的值就加一,当引用失效的时候,计数器就减一 ,任什么时候刻计数器为0的对象就是不可能再被使用的时候。编程

​ 这个算法看似不错并且简单,不过存在这一个致命伤(当两个对象互相引用的时候,就永远不会被回收)安全

public class Obj{
  public Object instance=null;
}

Obj a=new Obj();
Obj b=new Obj();

a.instance=b;
b.instance=a;

a=null;
b=null;

因而引用计数算法就永远回收不了这两个对象,下面介绍另外一种算法。并发

可达性分析算法

​ 经过一系列被称为“GC Roots”的对象做为起始点,从这些接点向下搜索,搜索所走过的路径称为引用链,当一个对象与任何一个引用链没有关联的时候则能够被回收。编程语言

Java中,可做为GC Roots的对象包括下面集中学习

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(Native方法)引用的对象

浅谈引用

​ 不管是计数器算法仍是可达性分析算法,都与“引用”有关,下面是Java中4种引用,强度依次减弱线程

强引用

​ 强引用就是咱们平时最熟悉的code

Object obj=new Object();

​ 只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。对象

软引用

​ 发生gc的时候,若是JVM内存充足则不回收,用SoftReference类来实现软引用。展现一个例子

SoftReference<Object> softReference=new SoftReference<>(new Object());
        System.out.println("before gc  "+softReference.get());
        System.gc();
        System.out.println("after gc   "+softReference.get());

​ 如下是输出

before gc  java.lang.Object@2752f6e2
after gc   java.lang.Object@2752f6e2

能够看到软引用的对象依然还在。

弱引用

​ 一旦发生gc,不管JVM内存充足与否,都会回收掉,用WeakReference类来实现弱引用。展现一个例子

WeakReference<Object> softReference=new WeakReference<>(new Object());
        System.out.println("before gc  "+softReference.get());
        System.gc();
        System.out.println("after gc   "+softReference.get());

​ 如下是输出

before gc  java.lang.Object@2752f6e2
after gc   null

弱引用的对象已经被回收!

虚引用

​ 虚引用也称为幽灵引用或者幻影引用,是最弱的一种引用,没法经过虚引用来取得一个对象实例,拥有虚引用的对象能够在任什么时候候被垃圾回收器回收。惟一的目的就是能在当这个对象被回收的时候收到一个系统通知,可用PhantomReference类实现虚引用。

回收方法区

​ 垃圾回收大多发生在Heap(堆区),由于在方法区进行垃圾回收效率较低,要断定一个类是不是“无用的类”条件比较苛刻,类须要同时知足下面3个条件才能算是无用的类

  • 该类全部实例已经被回收,堆中不存在该类任何实例
  • 加载该类的ClassLoader已经被回收
  • 该类对应的java.lang.Class对象没有在任何地方被引用,没法在任何地方经过反射访问该类的方法。

何时回收?when?

​ 首先,GC又分为minor GC 和 Full Gc(也称为Major GC)。Java 堆内存分为新生代和老年代,新生代中又分为1个Eden区域 和两个 Survivor区域。

那么对于 Minor GC 的触发条件:大多数状况下,直接在 Eden 区中进行分配。若是 Eden区域没有足够的空间,那么就会发起一次 Minor GC;对于 Full GC(Major GC)的触发条件:也是若是老年代没有足够空间的话,那么就会进行一次 Full GC。

注意:上面所说的只是通常状况,实际上,须要考虑一个空间分配担保的问题:

在发生Minor GC以前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代全部对象的总空间。若是大于则进行Minor GC,若是小于则看HandlePromotionFailure设置是否容许担保失败(不容许则直接Full GC)。若是容许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,若是大于则尝试Minor GC(若是尝试失败也会触发Full GC),若是小于则进行Full GC。

System.gc()就是指的Full GC

可是,具体程序执行到什么位置才会自动gc,这儿提两个概念Safepoint和SafeRegion。

安全点(Safepoint)

​ 安全点的选定是以程序“是否具备让程序长时间执行的特征”为标准选定的,“长时间执行”最明显的特征就是

  1. 指令序列复用
  2. 循环跳转
  3. 异常跳转等

对于Safepoint如何在GC发生时让全部线程都跑到最近的安全点上停下来,有两种方案抢先式中断主动式中断

抢先式中断:不须要线程执行的代码主动配合,GC发生时,首先把全部线程所有中断,若是发现有线程中断的地方不在安全点上,就恢复线程,让它“跑到”安全点上。(这种方案几乎不多使用)

主动式中断 :当GC须要中断线程的时候,不直接对线程操做,仅仅简单设置一个标志,各个线程执行时主动去轮询这个标志,发现为true就自动挂起,轮询的地方和安全点是重合的。

安全区域(Saferegion)

​ 当线程处于Sleep状态或者Blocked状态,这时候线程没法响应JVM的中断请求,“走”到安全的地方去中断挂起,JVM也显然不太可能等待线程从新被分配CPU时间,这时候就须要安全区域来解决。

​ 当线程执行到Safe Region中的代码时,首先标识本身已经进入了Safe Region,当JVM在发起GC时,就不用管标识本身为Safe Region状态的线程了。在线程要离开Safe Region时,他要检查系统是否完成了整个GC过程,若是完成了,线程就继续执行,不然必须等待直到收到能够安全离开Safe Region的信号为止。

如何回收?how?

标记-清除 算法

​ 算法分为标记清除两个阶段,首先标记出全部须要回收的对象,在标记完成后统一回收全部被标记的对象。他的不足主要有两个:

  1. 效率问题:标记和清除两个过程的效率都不高
  2. 空间问题:标记清除以后,会产生大量不连续的内存碎片,空间碎片太多可能会致使以后分配大对象时,没法找到足够的连续内存而提早触发GC

复制算法

​ 复制算法能够将容量划分为大小相等的两块,每次只使用其中的一块,当一块内存被用完了就将还存活的对象一次复制到另外一块内存上,而后把已经使用过的内存一次清理掉。不过所以内存缩小为原来的一半,代价太高。

​ 如今的商业虚拟机广泛采用这种算法来回收新生代,将新生代分为较大的一块Eden空间和两块较小的Survivor空间,HotSpot默认其比例为8:1:1,使用时,每次使用Eden加上其中一块Survivor,回收时,将Eden和Survivor中还存活的对象一次性复制到另外一块Survivor中,最后清理掉Eden和刚才用过的Survivor区。

标记-整理 算法

​ 标记过程与“标记-清除”算法同样,而后让全部存活的对象都向同一端移动,而后直接清理端边界之外的内存。

分代收集算法

​ 将Java堆分为新生代和老年代,在新生代中每次垃圾回收都有大批对象死去,少许存活,可使用复制算法,而老年代中对象存活率高没有额外的空间对它进行分配担保,必须使用“标记-整理”或者“标记-清理”算法来回收。

谈谈G1垃圾收集器

​ 在最近更新的JDK9中,JVM默认的垃圾收集器切换成了G1,那么G1有什么特色呢?总结如下最大的四个特色

  • 并行与并发:G1能充分利用多CPU、多核环境下的硬件优点,使用多个CPU,(CPU或者CPU核心)来缩短Stop-The-World停顿的时间,部分其余收集器须要停顿Java线程执行的GC动做,G1能够经过并发的方式让Java继续执行。
  • 分代收集:G1不须要其余收集器配合就能独立管理整个GC堆,G1采用不一样的方式去处理新建立的对象已经存活一段时间的对象熬过屡次GC的旧对象以获取更好的收集效果。
  • 空间整合:G1从总体来看是基于“标记-整理”算法,局部上来看是基于复制算法,这两种算法都不会产生内存碎片,有利于程序长时间运行。
  • 可预测的停顿:G1除了追求低停顿外,还能创建可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片断内,消耗在垃圾收集上的时间不超过N毫秒。

资料来源

《深刻理解Java虚拟机》P61-P84
相关文章
相关标签/搜索