53

Java虚拟机的垃圾回收机制

 5 years ago
source link: https://blog.luhuancheng.com/2019/03/29/Java虚拟机的垃圾回收机制/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在Java语言中,程序员通过关键字new来创建对象,当使用完创建出来的对象后,程序员无需手动释放内存。这部分释放内存的工作就叫做垃圾收集,由JVM来承担。

哪些内存需要回收

JVM将内存划分为几个区域:程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区(JDK8中已被废弃,采用名为MetaSpace的区域来替代,这部分内存使用native memory即本地内存)。以上的==程序计数器、Java虚拟机栈、本地方法栈不用考虑回收问题==。因为都是线程私有的区域,随着线程的产生而产生、线程的销毁而销毁,并且这部分空间大小的分配、回收,在编译期间就可以确定,当方法结束或者线程结束时进行回收。

Java堆和方法区(==JDK8改为MetaSpace,这部分待查阅资料后补充==),由于一个类的不同实现类需要的内存不一样,一个方法的多个分支创建的对象也不同,这一部分内存在编译期是不确定的,只有真正运行程序时才能明确。==Java堆和方法区,这部分内存的分配和回收都是动态的,垃圾回收机制也是在这一个区域内讨论==

如何确定对象需要回收

在垃圾收集之前,必须先知道哪些对象不再被任何其他途径使用了。垃圾收集器进行处理的也是这一类对象。

引用计数算法(Python采用)

为对象保存一个引用计数器,当对象被一个地方引用时,就将这个计数器加一,有一个地方取消引用时,就将计数器减一。任何时刻计数器为0,就说明这个对象不可能再被使用

可达性分析(HotSpot JVM采用)

可达性分析(Reachability Analysis)通过一系列的”GC Root”的对象作为起始点,从这些节点向下搜索,经过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连的话,这个对象不可达。判定为可以被回收的对象

Java中的GC Roots

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

四种引用类型

强引用、软引用、弱引用、虚引用

回收过程

==为什么要进行两次标记??==

VnAzIzE.png!web

方法区回收

方法区回收的是:废弃的变量、无用的类

废弃的变量

无用的类

  • 类的所有实例都已经被回收,Java堆中没有该类的实例
  • 加载该类的ClassLoader已经被回收
  • 该类对应的java.lang.Class没有在任何地方被引用,无法再任何地方通过反射访问该类方法
    相关VM Options:-verbose:class -XX:+TraceClassLoading -XX:+TraceClassUnLoading

垃圾收集算法

几种收集算法的思想、优缺点和发展过程

标记-清除算法

分为标记、清除两个阶段。

缺点

  • 标记和清除两个过程都需要对对象进行遍历,效率不高
  • 标记清除后会产生大量的不连续的内存碎片

复制算法

将内存按容量分为两块同样大小的区域,每次只使用其中一块。当一块内存用完就将存活的对象复制到另一块区域中,之后清除上一块区域的空间。

优点

  • 相对于标记-清除算法,复制算法效率高,而且不会产生内存碎片

缺点

  • 浪费了一半内存空间

标记-整理算法(Mark-Compact)

复制算法在对象存活率高的情况,需要较多的复制操作,效率变低。

标记-整理的标记过程和标记-清除算法的标记过程一致,只是在标记之后,并不是进行清除操作,而是让所有存活对象都向一端移动。最后直接清除边界以外未被存活对象占用的空间。

优点

  • 不会产生内存碎片
  • 不浪费内存空间

缺点

  • 整理需要计算量,拉低了效率

分代收集算法

复制算法和标记-整理算法,都具有不同的优点和缺点,并且适用于不同的场景。现代虚拟机采用分代收集算法,在不同存活周期的对象区域应用这两种收集算法(复制、标记-整理)。

  • 由于新生代对象的存活率较低,因此适用于使用复制算法。而且新生代内存区域分为一个Eden区、两个Survivor区。每次只使用一个Eden区、一个Survivor区(这两个区域的内存与新生代内存区域总量占比默认为8:1)

  • 老年代对象存活率较高,因此采用标记-整理算法减少复制过多对象的情况

如何发起内存回收

vAFzUn2.png!web

如何进行内存回收动作,即垃圾收集器如何工作

aMnaMbQ.png!web

Serial收集器

单线程,使用复制算法,用于回收新生代的收集器。 触发GC时,Stop The World

使用场景:

  • Client模式下的JVM

ParNew收集器

多线程,使用复制算法,用于回收新生代的收集器。其实就是Serial收集器的多线程版本。默认启用与服务器相同核心的线程数;触发GC时,Stop The World

使用场景:

  • 多核CPU的服务器下,可以充分利用CPU资源提高回收效率

Parallel Scavenge收集器(吞吐量为目标)

多线程,使用复制算法,用于回收新生代的收集器,以吞吐量为目标。(吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)

关键参数:

  • -XX:MaxGCPauseMillis 控制最大垃圾收集停顿时间
  • -XX:MaxTimeRatio 设置吞吐量大小
  • -XX:+UseAdaptiveSizePolicy 虚拟机根据运行状态自动调节新生代最大内存、Eden与Survivor的比例、晋升老年代对象的大小阀值

Serial Old收集器

Serial的老年代版本,使用标记-整理算法,用于回收老年代的收集器。与Parallel Scavenge搭配使用;==作为CMS收集器发生Concurrent Model Failure时的后备预案==

Parallel Old收集器

Parllel Scavenge的老年代版本,使用标记-整理算法。在JDK1.6中提供,==与Parallel Scavenge搭配使用,在注重吞吐量以及CPU资源敏感的场合优先考虑==

CMS收集器(Concurrent Mark Sweep)

基于标记-清除算法,以获取最短回收停顿时间为目标的收集器。适用于互联网网站、B/S系统的服务端场景

MviEFvr.png!web

缺点:

  • 对CPU资源敏感,由于并发标记、并发清除这两个阶段会启动线程与用户线程并发运行,因此对服务器会有CPU资源消耗。
  • 由于并发标记、并发清除是与用户线程并发运行的,因此需要预留一定的空间,避免在进行回收阶段时,没有足够的空间供用户线程使用。这将触发Concurrent Model Failure,将使用Serial Old来完成回收操作,导致回收时间过长
  • 由于CMS基于标记-清除算法,因此会产生大量内存碎片。会造成老年代区有很大空间剩余,但是无法分配大对象,这时需要触发一次Full GC。通过参数-XX:+UseCMSCompactAtFullCollection开关参数(默认开启),用于进行Full GC时开启内存碎片的合并整理过程(内存整理的过程是无法并发的,==Stop The World==),-XX:CMSFullGCsBeforeCompaction,用于设置多少次不压缩Full GC后,进行一次带压缩的Full GC(默认为0,即每次Full GC都进行压缩)

G1收集器(Garbage-First Garbage Collector)

垃圾收集器总结

参考 www.fasterj.com

在JVM中的进程名 | 用于收集哪个内存区域 | 启用命令行参数 | 描述 | 组合搭档

—|—|—|—|—

Copy | 新生代 | -XX:+UseSerialGC | 使用复制算法,单线程收集 | MarkSweepCompact

PS Scavenge | 新生代 | -XX:+UseParallelGC | 使用复制算法,多线程并发收集 | PS MarkSweep

ParNew | 新生代 | -XX:+UseParNewGC | 使用复制算法,多线程并发收集 | MarkSweepCompact

G1 Young Generation | 新生代 | -XX:+UseG1GC | 整体是标记-整理算法,region之间使用复制算法 | G1 Mixed Generation

MarkSweepCompact | 老年代 | -XX:+UseSerialGC | 使用标记-清除算法,可选的整理算法,单线程收集

PS MarkSweep | 老年代 | -XX:+UseParallelOldGC | 多线程标记-整理算法

ConcurrentMarkSweep | 老年代 | -XX:+UseConcMarkSweepGC | 标记-清除算法并发收集,较短的Stop-The-World时间,如果发生Concurrent-Mode-Failure时,使用MarkSweepCompact收集器作为预案

G1 Mixed Generation | 老年代 | -XX:+UseG1GC | 使用’Garbage First’ 算法

如何查看Java进程使用什么垃圾收集器?

// 通过MXBean来获取垃圾收集器的信息
List<GarbageCollectorMXBean> beans = ManagementFactory.getGarbageCollectorMXBeans();

for (GarbageCollectorMXBean bean : beans) {
    System.out.println(bean.getName());
}

环境

java -XX:+PrintCommandLineFlags -version

输出结果为

PS Scavenge
PS MarkSweep

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK