JVM中的垃圾回收机制

Posted IT94

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM中的垃圾回收机制相关的知识,希望对你有一定的参考价值。

在Java中,由于引入了JVM,所以我们不用再向C/C++那样创建对象之后手动的去申请和释放内存,这一切都交给了虚拟机来完成,而为了我们更好的了解对象的内存分配情况,更是为了优化系统,所以我们必然要去了解JVM的内存结构以及垃圾回收机制。

JVM内存结构

我们通过两张图来记忆其内存结构:

JVM-memory-structure

JVM中的垃圾回收机制
jvm-memory

JVM内存分为五部分:

  • 线程私有的:虚拟机栈、本地方法栈、程序计数器

    1. 本地方法栈:与虚拟机栈相似,只是它服务的对象是本地方法(Native Method)。

  • 线程共享的:堆和方法区

    1. 堆:堆中保存的是java对象本身以及数组。

    2. 方法区:存储的是类的信息(类名、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码。(注:方法区在JDK1.8之后被metaspace元数据区取代,存在于本地内存,其中存的是类的元数据,而常量池和类的静态变量存在Java堆中)。

  • 重点说一下java堆和方法区

    1. 堆又分为新生代和老年代,通俗的讲方法区又称为永久代(方法区不等价于永久代,只是因为方法区是由永久代来实现的),新生代又分为eden区和servivor-fromservivor-to区,在垃圾回收时使用。

    2. eden区存放的是新创建的对象,如果新创建的对象占用空间很大,则直接进入老年代,如果eden区空间存满,则会发生新生代的垃圾回收,即MinorGC

    3. servivor-from区存放的是上一次垃圾回收的存活对象,下一次垃圾回收时扫描此部分空间。

    4. servivor-to区存放的是由eden区和servivor-from区扫描的存活对象复制过来的。

    5. 老年代存放的是生命周期长的对象,一是对象创建时eden区放不下会直接进入老年代,二是在新生代垃圾回收多次后达到放入老年代阈值时存入老年代,默认此阈值时15。

    6. 老年代的垃圾回收不会频繁的发生(老年代垃圾回收叫MajorGC),因为老年代存放的是生命周期长的对象。

什么时候发生垃圾回收

  • 新生代垃圾回收时机

    • eden区空间不足

  • 老年代垃圾回收时机

    • 新创建的大对象放入老年代,而老年代空间不足时发生

    • 新生代对象达到晋升阈值时,进入老年代时空间不足

  • 永久代

    • 主程序运行期间不会对永久代进行垃圾回收,只会随着类信息的不断加载而胀满,最终导致OOM。

查找存活对象的方法

引用计数法

在堆中存储对象时,会在对象头维护一个计数器,当增加一个该对象的引用计数器就会加一,引用关系消失则计数器减一,当计数器值为零时就可以标记该对象为可回收对象。但是引用计数法存在一个问题,就是循环引用:如果A对象引用了B对象,而B对象引用了A对象,此时就造成了类似死锁的循环,计数器永远不会为零,这就导致了GC永远不会回收这两个对象

可达性分析

此方法通过以GCRoots为起点查找关联对象,如果存在到达对象的路径,就说明该对象时存活对象,而找不到到达对象的路径则说明对象可能是可回收对象,因为标记为可回收对象要求必须至少经过两次不可达标记,如果两次之后依然是可回收对象,就将面临垃圾回收。

垃圾回收算法

复制算法(copying)

将堆空间分为等大小的两部分,每次只使用其中的一块,如果一块内存满了之后就会将存活对象复制到另一块上去,把已使用的内存清空。此算法可以解决内存碎片化的缺陷。
如图:

JVM中的垃圾回收机制
Copying

标记-清除算法(mark-sweep)

首先是标记可回收对象,然后将可回收对象所在的空间清空。如图:

JVM中的垃圾回收机制
mark-sweep

标记-整理算法(mark-compact)

先标记可回收对象,回收完成后将存活对象移向内存的一端,然后清空端外内存。如图:

mark-compact

分代回收算法

将内存空间根据对象生命周期的长短划分为不同的区域,堆内存分为新生代和老年代。新生代会发生频繁的GC,而老年代GC频率较低,每次回收对象较少。所以可以根据不同的区域来使用不同的垃圾回收算法。

分区回收算法

将堆空间划分为相同大小的小区间,每个小空间单独使用,单独回收,这样可以控制每次回收的小区间个数,从而控制每次GC用户线程的停顿时间。

STW:stop the world,GC时会使用户线程停顿,此过程叫做STW。

垃圾收集器

新生代和老年代采用不同的垃圾收集器

  • 新生代:serial收集器、 parNew收集器、 parallel scavenge收集器

  • 老年代:serial old收集器、 parallel old收集器、 CMS(concurrent mark sweep)收集器

  • G1(garbage first)收集器

Serial收集器

Serial收集器是单线程的、采用复制算法。在收集过程中只使用一条线程,并且收集过程中必须暂停其他的工作线程,直到垃圾收集结束。

ParNew收集器

这种收集器其实是Serial收集器的多线程版,也是采用复制算法,使用多条线程去完成垃圾收集,同时收集过程中也会暂停其他工作线程。默认启用CPU个数条线程,可以使用-XX:ParallelGCThreads参数来修改启用的线程数。

Parallel Scavenge收集器

Parallel Scavenge收集器也是采用多线程的复制算法,和ParNew收集器的不同在于可以控制吞吐量(吞吐量=用户代码运行时间/(用户代码运行时间+垃圾收集时间)),同样也会暂停工作线程。

Serial Old收集器

单线程的,采用标记-整理算法,会暂停用户线程。

Parallel Old收集器

多线程的,标记-整理算法,可以控制吞吐量,会暂停工作线程。

CMS收集器

多线程的,采用标记-清除算法,主要是为了保证垃圾收集期间最短的工作线程停顿。
过程:

  • 初始标记,标记GCRoots可以直接关联的对象,需要暂停用户线程。

  • 并发标记,从上一阶段标记的对象出发,标记所有可达到的对象,此阶段和用户线程并发执行,不会暂停用户线程。

  • 重新标记,修正并发标记阶段产生变动的对象,此阶段多线程执行,暂停用户线程。

  • 并发清除,和用户线程并发执行,清除GCRoots不可达对象,不会暂停用户线程。

G1收集器

G1收集器相对于CMS收集器的改进:

  • 采用标记-整理算法,不会产生内存碎片。

  • 可以精确控制STW的时间,并且不会牺牲吞吐量,该收集器不会全区域回收,而是把堆空间分为固定大小的几个区域,跟踪其垃圾回收进度,然后维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾最多的区域。区域划分和优先级区域回收机制,确保G1收集器可以在有限时间获得最高的垃圾收集效率。



最近热文:








如果你喜欢本文。

请长按二维码,关注IT94

以上是关于JVM中的垃圾回收机制的主要内容,如果未能解决你的问题,请参考以下文章

jvm内存回收机制

JVM的内存区域划分以及垃圾回收机制详解

JVM虚拟机:JVM 垃圾回收机制概念及其算法

JVM中的垃圾回收机制

JVM的垃圾回收机制 总结(垃圾收集回收算法垃圾回收器)

谈谈JVM垃圾回收机制及垃圾回收算法