小师妹学JVM之:GC的垃圾回收算法

简介: 小师妹学JVM之:GC的垃圾回收算法

目录



简介



JVM的重要性不言而喻了,如果把java的应用程序比作一辆跑车,那么JVM就是这辆车的发动机,没有它,java程序就成了空中楼阁,无根浮萍。而在JVM中有一块内存区域叫做运行时数据区域,存储了运行时所需要的所有对象,而Heap Area则是其中最大的一块。

内存毕竟不是无限的,所以就需要一种机制来将不再使用的对象进行回收,这种机制就是今天我们要讲的GC。


更多精彩内容且看:



对象的生命周期



小师妹:F师兄,你相信这个世界有轮回吗?


师兄我是一个坚定的无神论者,活在当下就好了,何必操心后面的轮回呢?


小师妹:F师兄,这个你就不懂了,意识是组成脑的原子群的一种组合模式,我们大脑的物质基础和一块石头没有什么不同。当我们掌握大脑的组合方式,然后重构,我们的意识就重现了,这就是轮回。这可是量子理论中提到的观念哦。


哇,小师妹什么时候这么厉害了,都开始探讨这么高深的话题了。F师兄我实在是跟不上节奏啊。


小师妹,F师兄,我是怕你尴尬,想引出java对象的生命周期这个话题嘛。


量子理论我不熟,java对象我还没怕过谁。


对象的生命周期其实很简单:创建,使用中,最后被销毁。


  1. 创建对象


举个最简单的创建对象的例子:


Object obj = new Object();


对象创建的时候,将会为该对象分配特定的空间。


  1. 使用对象

对象创建之后,就可以被其他的对象使用,如果其他的对象有使用该对象,那么我们成为该对象被引用了。


  1. 对象销毁

当一个对象没有被其他对象引用的时候,我们就称为该对象可以被回收了。在Java中,对象的回收是由GC来负责的。


垃圾回收算法



小师妹:F师兄,我觉得垃圾回收好像挺简单的,我们为每个对象维持一个指针计数器,每引用一次就加一,这样不就可以实现垃圾回收器了吗?


底层原理是这么一个道理,但是JVM需要一种更加高效的算法来保证垃圾回收的效率,同时也不会影响正在运行的程序。


接下来我们将会介绍一下,在JVM中比较常用几个垃圾回收算法:


Mark and sweep



Mark and sweep是最最简单的垃圾回收算法,简单点讲,它可以分为两个步骤:


  1. 标记live对象


标记live对象听起来很简单,就是扫描堆中的对象,看这些对象是否被引入。


但是这里有一个问题,如果是两个对象互相引用的时候,而这两个对象实际上并没有被外部的对象所引用,那么这两个对象其实是应该被回收的。所以我们还需要解决一个关键性的问题:从哪里开始扫描的问题。


JVM定义了一些Root对象,从这些对象开始,找出他们引用的对象,组成一个对象图。所有在这个图里面的对象都是有效的对象,反之不在对象图中的对象就应该被回收。有效的对象将会被Mark为alive。


这些Root对象包括:正在执行的方法中的本地对象和输入参数。活动的线程,加载类中的static字段和JNI引用。


注意,这种遍历其实是有个缺点的,因为为了找到对象图中哪些对象是live的,必须暂停整个应用程序,让对象变成静止状态,这样才能构建有效的对象图。后面我们会介绍更加有效的垃圾回收算法。


  1. 删除对象


扫描对象之后,我们就可以将未标记的对象删除了。


删除有三种方式,第一种方式是正常删除。但是正常删除会导致内存碎片的产生。所以第二种方式就是删除之后进行压缩,以减少内存碎片。还有一种方式叫做删除拷贝,也就是说将alive的对象拷贝到新的内存区域,这样同样可以解决内存碎片的问题。


Concurrent mark sweep (CMS)



在讲CMS之前,我们先讲一下垃圾回收器中的Eden,Old和Survivor space几个大家应该都很熟悉的分代技术。


Young Gen被划分为1个Eden Space和2个Suvivor Space。当对象刚刚被创建的时候,是放在Eden space。垃圾回收的时候,会扫描Eden Space和一个Suvivor Space。如果在垃圾回收的时候发现Eden Space中的对象仍然有效,则会将其复制到另外一个Suvivor Space。


就这样不断的扫描,最后经过多次扫描发现任然有效的对象会被放入Old Gen表示其生命周期比较长,可以减少垃圾回收时间。


image.png


之后要将的几个垃圾回收器,除了ZGC,其他都使用的是分代的技术。


好了,现在继续讲CMS,CMS是mark and swap的升级版本,它使用多个线程来对heap区域进行扫描,从而提升效率。


CMS在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是mark-sweep。


使用CMS的命令很简单:


-XX:+UseConcMarkSweepGC


image.png


上面是列出的一些CMS的调优参数。


Serial garbage collection



Serial garbage collection使用单一的线程来进行垃圾回收操作,其好处就是不需要和其他的线程进行交互。如果你是单核的CPU,那么最好就是选择Serial garbage collection,因为你不能充分利用多核的好处。同样的它也常常用在比较小型的项目中。


Serial garbage collection在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是 mark-sweep-compact。


下面是开启命令:


-XX:+UseSerialGC


Parallel garbage collection



和serial GC类似,它在Young Generation中使用的是mark-copy,而在Old Generation主要使用的是 mark-sweep-compact。不同的是它是并行的。


可以通过下面的命令来指定并发的线程:


-XX:ParallelGCThreads=N


如果你是多核处理器,那么Parallel GC可能是你的选择。


Parallel GC是JDK8中的默认GC。而在JDK9之后, G1是默认的GC。


使用下面的命令来开启Parallel GC:


-XX:+UseParallelGC


G1 garbage collection



为什么叫G1呢,G1=Garbage First,它是为替换CMS而生的,最早出现在java7中。


G1将heap区域划分成为多个更小的区域,每个小区域都被标记成为young generation 或者old generation。从而运行GC在更小的范围里运行,而不是影响整个heap区域。


可以使用下面的命令来开启:


-XX:+UseG1GC


Z Garbage Collection



ZGC是一个可扩展的,低延迟的GC。ZGC是并发的,而且不需要停止正在运行的线程。

使用下面的命令来开启:


-XX:+UseZGC


ZGC是在JDK11中被引入的。


怎么选择



小师妹:F师兄,你讲了这么多个GC,到底我该用哪个呢?


高射炮不能用来打蚊子,所以选择合适的GC才是最终要的。这里F师兄给你几个建议:


  1. 如果你的应用程序内存本来就很小,那么使用serial collector : -XX:+UseSerialGC.
  2. 如果你的程序运行在单核的CPU上,并且也没有程序暂停时间的限制,那么还是使用serial collector : -XX:+UseSerialGC.
  3. 如果对峰值期的性能要求比较高,但是对程序暂停时间没多大的要求,那么可以使用 parallel collector: -XX:+UseParallelGC。
  4. 如果更加关注响应时间,并且GC的对程序的暂停时间必须要小,那么可以使用-XX:+UseG1GC。
  5. 如果响应时间非常重要,并且你在使用大容量的heap空间,那么可以考虑使用ZGC: -XX:UseZGC。


总结



本文介绍了几种GC的算法,大家可以根据需要选用。

相关文章
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
65 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
18天前
|
存储 监控 Java
JVM进阶调优系列(8)如何手把手,逐行教她看懂GC日志?| IT男的专属浪漫
本文介绍了如何通过JVM参数打印GC日志,并通过示例代码展示了频繁YGC和FGC的场景。文章首先讲解了常见的GC日志参数,如`-XX:+PrintGCDetails`、`-XX:+PrintGCDateStamps`等,然后通过具体的JVM参数和代码示例,模拟了不同内存分配情况下的GC行为。最后,详细解析了GC日志的内容,帮助读者理解GC的执行过程和GC处理机制。
|
1月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
43 3
|
1月前
|
算法 Java
JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
本文详细介绍了JVM中的GC算法,包括年轻代的复制算法和老年代的标记-整理算法。复制算法适用于年轻代,因其高效且能避免内存碎片;标记-整理算法则用于老年代,虽然效率较低,但能有效解决内存碎片问题。文章还解释了这两种算法的具体过程及其优缺点,并简要提及了其他GC算法。
 JVM进阶调优系列(4)年轻代和老年代采用什么GC算法回收?
|
1月前
|
算法 JavaScript 前端开发
垃圾回收算法的原理
【10月更文挑战第13天】垃圾回收算法的原理
23 0
|
1月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
37 4
|
9天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
7天前
|
Java Linux Windows
JVM内存
首先JVM内存限制于实际的最大物理内存,假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制。
8 1
|
1月前
|
存储 缓存 算法
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!