JVM垃圾回收机制

191 阅读6分钟

JVM运行时数据区域

  1. 线程共享的内存区域,存放对象实例,几乎所有对象实例都在这里分配内存。也是垃圾回收的重点照顾对象。 可通过 -Xmx 和 -Xms 控制最大最小堆内存。

  2. 虚拟机栈

    线程私有的,生命周期与线程相同,描述java方法执行的内存模型:每个方法调用都会创建一个栈帧,用于存储局部变量 表、操作数栈,动态链接,方法出口等信息。每一个方法从调用到执行完成,就对应着一个栈帧在虚拟机栈中入栈都出栈 的过程。

  3. 本地方法栈

    线程私有的,与虚拟机栈的作用非常相似,只不过本地方法栈为虚拟机执行Native方法服务,而虚拟机栈为虚拟机执行java方法服务。

  4. 方法区

    线程共享的,存放已加载的类的信息、常量、静态变量。

  5. 运行时常量池

    线程共享的,是方法区的一部分,用于保存编译期生成的各种字面量和符号引用。例如:

  6. 程序计数器

    线程私有的,记录当前线程执行字节码的行号指示器。

如何确定哪些内存需要进行回收

  1. 引用计数器法

    基本思想:给对象添加一个计数器,每当有一个地方引用它时,计数器的值加一;当引用失效时,计数器的值减一。

    缺点:不能解决循环引用,即:A中引用B,B中引用A,此时A,B回收不了

    优点:实现简单,判定效率很高

  2. 可达性分析算法

    基本思想:从一系列的 GC Roots 的对象作为起点,所走过的路径成为引用链,当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可达的。

    优点:解决了对象间循环引用的问题

    缺点:实现起来比较复杂

    哪些对象可以作为GC Roots

    1.虚拟机栈(栈帧中本地变量表)中引用的对象

    2.方法区中静态属性引用的对象

    3.方法去中常量引用的对象

    4.本地方法栈中JNI(Native方法)引用的对象

垃圾回收算法

  1. 标记清除算法

    基本思想:1.标记所有可以回收的对象;2.回收被标记的对象

    缺点:1.标记和清除效率不高;2.会产生大量的空间碎片

  2. 复制算法

    基本思想:把内存空间分为两块,每次只是用其中的一块,当这一块的内存用完了,就将存活的对象复制到另一块上,然后清理已使用的空间。

    缺点:会浪费一部分内存

     ps:现在商业虚拟机新生代基本都是采用这种算法,因为java中的大部分对象都是朝生夕死的,所以并不需要按1:1的比例来分配内存空间,
     而是将内存分为一块较大的Eden和两块较小的Survivor(from Survivor和to Survivor)。当回首时,将Eden和from Survivor中还存
     活的对象复制到to Survivor中,然后清理Eden 和 from Survivor中的空间。HotSpot中 Eden :from Survivor : to Survivor的
     比例是8:1:1。这里需要注意,当存活的对象to Survivor中放不下时,这时候需要依赖老年代进行分配担保,也就是说to Survivor放不
     下直接放进老年代中
    
  3. 标记整理算法

    基本思想:先标记可回收的对象,然后将所有存活的对象移到内存的一端,保证存放对象的内存都是连续的,然后在清除掉端边界以外的内存。

    优点:避免了产生内存碎片问题

  4. 分代收集算法

    基本思路:根据对象的存活周期讲对象分为新生代和老年代,根据各个年代的特点去选择合适的收集算法,在新生代每次存活的对象都很少,就选用复制算法,在老年代每次对象存活率都很高,没有额外的空间进行分配担保,所以选用标记清除或者标记整理算法。

     ps:当前商用的虚拟机垃圾收集都采用分代收集算法。
    

垃圾收集器

新生代收集算法

  1. Serial:复制算法,单线程收集
  2. ParNew:复制算法,Serial的多线程版本,多线程并行收集
  3. Parallel Scavenge:复制算法,关注吞吐量(用户程序运行时间/(用户程序运行时间 + 垃圾回收时间))

老年代收集算法

  1. Serial old : Serial的老年代算法,单线程收集,采用标记整理算法

  2. Parallel Old : Parallel Scavenge的老年代算法,采用标记整理算法,关注吞吐量

  3. CMS:老年代回收算法,关注停顿时间

    过程:1.初始标记;2.并发标记;3.重新标记;4.并发清除 初始标记和重新标记需要Stop the world,但时间很短。比较耗时的标记、清除工作和用户线程并发执行,所以用户感知非常小。 优点:STW时间大大减小,做到了最短停顿时间。

    缺点:1.对cpu资源敏感,默认分配给垃圾回收器的cpu核心数是(cpu数量 + 3)/4 个,当cpu资源较少是,比如只有两个,垃圾回收器就占用了一半的cpu,程序执行影响较大。2.无法处理浮动垃圾,因为和用户线程并发运行,收集的过程中还可能产生新的垃圾,这些垃圾只能等到下一次垃圾收集时回收。3.会产生内存碎片,基于标记清除算法的垃圾收集器都会产生内存碎片。

  4. G1:

    过程:1.初始标记;2.并发标记;3.最终标记;4.筛选回收

    优点:1. 并行与并发,充分利用多核cpu资源来缩短Stop-The-World的时间。2.分代收集,不需要配合其他收集器,采用不同的方法处理创建的对象和存活了一段时间的对象、熬过多次的对象以获取更好的收集结果。3.空间整合,G1整体上看是基于标记整理算法,从局部(两个region之间)看起来又像是基于复制算法,这两种都不会产生内存碎片。这种特性有利于程序长时间运行,分配大对象时不会因为找不到连续的内存而提前出发下一次GC。4.可预测的停留:可以设置在指定的时间内垃圾回收的时间不超过N毫秒。

    G1的思想:G1将内存划分为大小相等的多个region,利用化整为零的思想,也分新生代和老年代,但新生代和老年代不是物理隔离的了,他们都属于region。G1之所以可以建立可预测的停顿时间模型,是因为G1追踪每一个region的可回收的内存大小以及所需时间的经验值,在后台维护一个优先列表,每次根据允许的收集时间,选择最合适的region进行收集。提升了回收的效率。