「译文」Java 垃圾收集参考手册(四):Serial GC

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 「译文」Java 垃圾收集参考手册(四):Serial GC

Serial GC(串行 GC)

Serial GC 对年轻代使用 mark-copy(标记 - 复制) 算法 , 对老年代使用 mark-sweep-compact(标记 - 清除 - 整理) 算法. 顾名思义, 两者都是单线程的垃圾收集器, 不能进行并行处理。两者都会触发全线暂停(STW), 停止所有的应用线程。

因此这种 GC 算法不能充分利用多核 CPU。不管有多少 CPU 内核, JVM 在垃圾收集时都只能使用单个核心。

要启用此款收集器, 只需要指定一个 JVM 启动参数即可, 同时对年轻代和老年代生效:

java -XX:+UseSerialGC com.mypackages.MyExecutableClass
RUBY

该选项只适合几百 MB 堆内存的 JVM, 而且是单核 CPU 时比较有用。 对于服务器端来说, 因为一般是多个 CPU 内核, 并不推荐使用, 除非确实需要限制 JVM 所使用的资源。大多数服务器端应用部署在多核平台上, 选择 Serial GC 就表示人为的限制系统资源的使用。 导致的就是资源闲置, 多的 CPU 资源也不能用来降低延迟, 也不能用来增加吞吐量。

下面让我们看看 Serial GC 的垃圾收集日志, 并从中提取什么有用的信息。为了打开 GC 日志记录, 我们使用下面的 JVM 启动参数:

-XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
RUBY

产生的 GC 日志输出类似这样(为了排版, 已手工折行):

2015-05-26T14:45:37.987-0200:
        151.126: [GC (Allocation Failure)
        151.126: [DefNew: 629119K->69888K(629120K), 0.0584157 secs]
        1619346K->1273247K(2027264K), 0.0585007 secs]
    [Times: user=0.06 sys=0.00, real=0.06 secs]
2015-05-26T14:45:59.690-0200:
        172.829: [GC (Allocation Failure)
        172.829: [DefNew: 629120K->629120K(629120K), 0.0000372 secs]
        172.829: [Tenured: 1203359K->755802K(1398144K), 0.1855567 secs]
        1832479K->755802K(2027264K),
        [Metaspace: 6741K->6741K(1056768K)], 0.1856954 secs]
    [Times: user=0.18 sys=0.00, real=0.18 secs]
MATHEMATICA

此 GC 日志片段展示了在 JVM 中发生的很多事情。 实际上, 在这段日志中产生了两个 GC 事件, 其中一次清理的是年轻代, 另一次清理的是整个堆内存。让我们先来分析前一次 GC, 其在年轻代中产生。

Minor GC(小型 GC)

以下代码片段展示了清理年轻代内存的 GC 事件:

2015-05-26T14:45:37.987-02001 : 151.12622 : [GC3 (Allocation Failure4 151.126:

[DefNew5 : 629119K->69888K6 (629120K)7 , 0.0584157 secs] 1619346K->1273247K8

(2027264K)9, 0.0585007 secs10] [Times: user=0.06 sys=0.00, real=0.06 secs]11

  1. 2015-05-26T14:45:37.987-0200 – GC 事件开始的时间. 其中 -0200 表示西二时区, 而中国所在的东 8 区为 +0800
  2. 151.126 – GC 事件开始时, 相对于 JVM 启动时的间隔时间, 单位是秒。
  3. GC – 用来区分 Minor GC 还是 Full GC 的标志。GC表明这是一次 小型 GC(Minor GC)
  4. Allocation Failure – 触发 GC 的原因。本次 GC 事件, 是由于年轻代中没有空间来存放新的数据结构引起的。
  5. DefNew – 垃圾收集器的名称。这个神秘的名字表示的是在年轻代中使用的: 单线程, 标记 - 复制(mark-copy), 全线暂停(STW) 垃圾收集器。
  6. 629119K->69888K – 在垃圾收集之前和之后年轻代的使用量。
  7. (629120K) – 年轻代总的空间大小。
  8. 1619346K->1273247K – 在垃圾收集之前和之后整个堆内存的使用情况。
  9. (2027264K) – 可用堆的总空间大小。
  10. 0.0585007 secs – GC 事件持续的时间, 以秒为单位。
  11. [Times: user=0.06 sys=0.00, real=0.06 secs]– GC 事件的持续时间, 通过三个部分来衡量:
  • user – 在此次垃圾回收过程中, 所有 GC 线程所消耗的 CPU 时间之和。
  • sys – GC 过程中中操作系统调用和系统等待事件所消耗的时间。
  • real – 应用程序暂停的时间。因为串行垃圾收集器 (Serial Garbage Collector) 只使用单线程, 因此 real time 等于 user 和 system 时间的总和。

可以从上面的日志片段了解到, 在 GC 事件中,JVM 的内存使用情况发生了怎样的变化。此次垃圾收集之前, 堆内存总的使用量为 1,619,346K。其中, 年轻代使用了 629,119K。可以算出, 老年代使用量为 990,227K

更重要的信息蕴含在下一批数字中, 垃圾收集之后, 年轻代的使用量减少了 559,231K, 但堆内存的总体使用量只下降了 346,099K。 从中可以算出, 有 213,132K 的对象从年轻代提升到了老年代。

此次 GC 事件也可以用下面的示意图来说明, 显示的是 GC 开始之前, 以及刚刚结束之后, 这两个时间点内存使用情况的快照:

04_01_serial-gc-in-young-generation.png

Full GC(完全 GC)

理解第一次 minor GC 事件后, 让我们看看日志中的第二次 GC 事件:

2015-05-26T14:45:59.690-02001 : 172.8292 : [GC (Allocation Failure 172.829:

[DefNew: 629120K->629120K(629120K), 0.0000372 secs3] 172.829:[Tenured4:

1203359K->755802K5 (1398144K)6, 0.1855567 secs7 ] 1832479K->755802K8

(2027264K)9, [Metaspace: 6741K->6741K(1056768K)]10

[Times: user=0.18 sys=0.00, real=0.18 secs]11

  1. 2015-05-26T14:45:59.690-0200 – GC 事件开始的时间. 其中 -0200 表示西二时区, 而中国所在的东 8 区为 +0800
  2. 172.829 – GC 事件开始时, 相对于 JVM 启动时的间隔时间, 单位是秒。
  3. [DefNew: 629120K->629120K(629120K), 0.0000372 secs – 与上面的示例类似, 因为内存分配失败, 在年轻代中发生了一次 minor GC。此次 GC 同样使用的是 DefNew 收集器, 让年轻代的使用量从 629120K 降为 0。注意,JVM 对此次 GC 的报告有些问题, 误将年轻代认为是完全填满的。此次垃圾收集消耗了 0.0000372 秒。
  4. Tenured – 用于清理老年代空间的垃圾收集器名称。Tenured 表明使用的是单线程的全线暂停垃圾收集器, 收集算法为 标记 - 清除 - 整理(mark-sweep-compact)。
  5. 1203359K->755802K – 在垃圾收集之前和之后老年代的使用量。
  6. (1398144K) – 老年代的总空间大小。
  7. 0.1855567 secs – 清理老年代所花的时间。
  8. 1832479K->755802K – 在垃圾收集之前和之后, 整个堆内存的使用情况。
  9. (2027264K) – 可用堆的总空间大小。
  10. [Metaspace: 6741K->6741K(1056768K)] – 关于 Metaspace 空间, 同样的信息。可以看出, 此次 GC 过程中 Metaspace 中没有收集到任何垃圾。
  11. [Times: user=0.18 sys=0.00, real=0.18 secs]– GC 事件的持续时间, 通过三个部分来衡量:
  1. user – 在此次垃圾回收过程中, 所有 GC 线程所消耗的 CPU 时间之和
  2. sys – GC 过程中中操作系统调用和系统等待事件所消耗的时间。
  3. real – 应用程序暂停的时间。因为串行垃圾收集器 (Serial Garbage Collector) 只使用单线程, 因此 real time 等于 user 和 system 时间的总和。

和 Minor GC 相比, 最明显的区别是 —— 在此次 GC 事件中, 除了年轻代, 还清理了老年代和 Metaspace. 在 GC 事件开始之前, 以及刚刚结束之后的内存布局, 可以用下面的示意图来说明:

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2月前
|
算法 安全 Java
Java内存管理:深入理解垃圾收集器
在Java的世界里,内存管理是一块基石,它支撑着应用程序的稳定运行。本文将带你走进Java的垃圾收集器(GC),探索它是如何默默守护着我们的内存安全。我们将从垃圾收集的基本概念出发,逐步深入到不同垃圾收集器的工作机制,并通过实例分析它们在实际应用中的表现。文章不仅旨在提升你对Java内存管理的认识,更希望你能通过这些知识优化你的代码,让程序运行更加高效。
53 3
|
3月前
|
监控 算法 Java
Java内存管理:垃圾收集器的工作原理与调优实践
在Java的世界里,内存管理是一块神秘的领域。它像是一位默默无闻的守护者,确保程序顺畅运行而不被无用对象所困扰。本文将带你一探究竟,了解垃圾收集器如何在后台无声地工作,以及如何通过调优来提升系统性能。让我们一起走进Java内存管理的迷宫,寻找提高应用性能的秘诀。
|
3月前
|
Kubernetes Cloud Native Java
云原生之旅:从容器到微服务的演进之路Java 内存管理:垃圾收集器与性能调优
【8月更文挑战第30天】在数字化时代的浪潮中,企业如何乘风破浪?云原生技术提供了一个强有力的桨。本文将带你从容器技术的基石出发,探索微服务架构的奥秘,最终实现在云端自由翱翔的梦想。我们将一起见证代码如何转化为业务的翅膀,让你的应用在云海中高飞。
|
3月前
|
存储 算法 Java
解释 Java 堆空间和垃圾收集
【8月更文挑战第22天】
36 0
|
1月前
|
监控 Java Linux
Java 性能调优:调整 GC 线程以获得最佳结果
Java 性能调优:调整 GC 线程以获得最佳结果
69 11
|
23天前
|
监控 安全 Java
Java Z 垃圾收集器如何彻底改变内存管理
大家好,我是V哥。今天聊聊Java的ZGC(Z Garbage Collector)。ZGC是一个低延迟垃圾收集器,专为大内存应用场景设计。其核心优势包括:极低的暂停时间(通常低于10毫秒)、支持TB级内存、使用着色指针实现高效对象管理、并发压缩和去碎片化、不分代的内存管理。适用于实时数据分析、高性能服务器和在线交易系统等场景,能显著提升应用的性能和稳定性。如何启用?只需在JVM启动参数中加入`-XX:+UseZGC`即可。
144 0
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
1月前
|
XML Java 数据格式
Java正则表达式大全(参考)
Java正则表达式大全(参考)
|
2月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制(GC)
本文将探讨Java的自动内存管理核心——垃圾回收机制。通过详细解析标记-清除算法、复制算法和标记-整理算法等常用垃圾回收算法,以及CMS、G1等常见垃圾回收器,帮助读者更好地理解Java应用的性能优化和内存管理。同时,探讨分代收集、分区收集等策略在实际项目中的应用。结语部分总结了垃圾回收机制在Java开发中的重要性,并展望了未来可能的发展。
52 0
|
3月前
|
存储 监控 算法
Java的垃圾收集器:理解与应用
【8月更文挑战第21天】在Java的世界里,垃圾收集器(Garbage Collector)是不可或缺的角色,它默默地在后台工作,保证内存的有效利用。本文将探讨Java的垃圾收集机制,包括其工作原理、常见的垃圾收集算法以及如何优化垃圾收集过程。通过了解这些内容,开发者可以编写出更高效、更稳定的Java应用程序。