垃圾回收器比较:CMS 和 G1
前言
在查看系统内存监控的过程中,发现有几台机器的内存使用率一直很高,而且是呈现一个不太正常的高度,初始以为是 GC 不完全,也就是 JVM 内有大量对象不能回收,于是采用 Arthas 诊断查看一下机器的 JVM 使用情况。
这是挑选的一台机器查看的 JVM 使用情况,上图截图部分为内存使用情况。主要看第一个 HEAP-MEMORY-USAGE。这是 jvm 中堆的使用情况,init 为堆初始化 3.0 GiB,used 为已经使用了 598 MiB,committed 为已提交内存,即空闲内存+使用内存,但是一直被 JVM 占用,并没有归还给操作系统,这就造成了我们在监控上看的时候这台机器的内存占用率一直高居不下的主要原因之一。
按照正常业务理解,Jvm 在触发 GC 后回收的空闲内存应该会释放一部分给操作系统,但实际上并没有这么做,下面通过一段代码测试验证 CMS 和 G1 的物理内存归还机制。
测试
在测试中,需要至少两个线程,一个用来不断的创建大对象,一个用来手动触发系统 GC。同时采用 JProfiler 监控 JVM 堆内存变化。
测试代码如下:
1 import java.util.ArrayList;
2 import java.util.List;
3
4 /**
5 * @Author: Li.Jincheng
6 * @Date: 2022/1/24 18:36
7 * @Description:
8 */
9 public class JvmMemoryTest {
10 static volatile List<BigObject> list = new ArrayList<>();
11
12 public static void main(String[] args) {
13 int count = 512;
14 Thread createObjectThread = new Thread(() -> {
15 try {
16 for (int i = 1; i <= 10; i++) {
17 System.out.println(String.format("第%s次生产%s大小的对象", i, count));
18 add(list, count);
19 //休眠10秒
20 Thread.sleep(10000);
21 }
22 } catch (InterruptedException e) {
23 e.printStackTrace();
24 }
25 });
26
27 Thread clearThread = new Thread(() -> {
28 while (true) {
29 if (list.size() < count) {
30 continue;
31 }
32 //当List内存到达512M,就通知GC
33 System.out.println("清理list.... 回收jvm内存....");
34 list.clear();
35 //GC
36 System.gc();
37 //打印堆内存信息
38 printJvmMemoryInfo();
39 }
40 });
41
42 // 启动线程
43 createObjectThread.start();
44 clearThread.start();
45
46 try {
47 Thread.currentThread().join();
48 } catch (InterruptedException e) {
49 e.printStackTrace();
50 }
51 }
52
53 /**
54 * 打印Jvm内存情况
55 */
56 public static void printJvmMemoryInfo() {
57 //虚拟机级内存情况查询
58 int byteToMb = 1024 * 1024;
59 Runtime runtime = Runtime.getRuntime();
60 long vmTotal = runtime.totalMemory() / byteToMb;
61 long vmFree = runtime.freeMemory() / byteToMb;
62 long vmMax = runtime.maxMemory() / byteToMb;
63 long vmUse = vmTotal - vmFree;
64 System.out.println("##############");
65 System.out.println("JVM内存已用的空间为:" + vmUse + " MB");
66 System.out.println("JVM内存的空闲空间为:" + vmFree + " MB");
67 System.out.println("JVM总内存空间为:" + vmTotal + " MB");
68 System.out.println("JVM总内存最大堆空间为:" + vmMax + " MB");
69 System.out.println("##############");
70 }
71
72 /**
73 * 创建大对象列表
74 *
75 * @param list
76 * @param count
77 */
78 public static void add(List<BigObject> list, int count) {
79 for (int i = 0; i < count; i++) {
80 BigObject bigObject = new BigObject();
81 list.add(bigObject);
82 try {
83 Thread.sleep(50);
84 } catch (InterruptedException e) {
85 e.printStackTrace();
86 }
87 }
88 }
89
90 public static class BigObject {
91 //生成5M的对象
92 private byte[] bytes = new byte[1024 * 1024 * 5];
93 }
94 }
CMS 垃圾回收器
配置:
-Xms128M -Xmx2048M -XX:+UseConcMarkSweepGC
结果:
如上图所示,蓝色部分为实际使用内存,绿色部分为空闲内存,从图上可以看出在开始的时候即使触发了 GC 操作,JVM 回收了内存,但是并没有立即将空闲内存归还操作系统。在第三次 gc 后,可以明显看到 JVM 的空闲空间明显下降,这表明 JVM 已经归还了一部分内存,后面,随着 GC 次数增加慢慢的将内存归还。也就是说,CMS 垃圾回收器最终也会将申请的内存归还操作系统。
G1 垃圾回收器
配置:
-Xms128M -Xmx2048M -XX:+UseG1GC
结果:
从上图可以看出,每触发一次 GC,JVM 的使用内存和空闲内存总和都降到了初始值 128M,也就是说在使用 G1 垃圾回收器时,每次 GC 都会将 JVM 新申请开辟的空间归还给操作系统。这也是一开始我们理解的垃圾回收机制以及预期结果。
总结
CMS 垃圾回收器,在 JVM 申请内存后,会随着 GC 次数增加和频率足见拉长,从继续申请内存到慢慢归还给操作系统,知道如图所示出现一次全部归还后,每一次的 GC 都会将剩余空间归还操作系统;
G1 垃圾回收器与之相反,每次 GC 后都会将内存全部归还操作系统,大大降低了机器的内存占用。
垃圾回收器比较:CMS 和 G1的更多相关文章
- [JVM 相关] Java 新型垃圾回收器(Garbage First,G1)
回顾传统垃圾回收器 HotSpot 垃圾收集器实现 Serial Collector(串型收集器) 使用场景,大多数服务器是单核CPU. 适用收集场景:1. 新生代收集(Young Generatio ...
- 垃圾回收之CMS、G1、ZGC对比
ZGC(The Z Garbage Collector)是JDK 11中推出的一款低延迟垃圾回收器,它的设计目标包括: 停顿时间不超过10ms: 停顿时间不会随着堆的大小,或者活跃对象的大小而增加: ...
- 探索ParNew和CMS垃圾回收器
前言 上篇文章我们一起分析了JVM的垃圾回收机制,了解了新生代的内存模型,老年代的空间分配担保原则,并简单的介绍了几种垃圾回收器.详细内容小伙伴们可以去看一下我的上篇文章:秒懂JVM的垃圾回收机制. ...
- G1垃圾回收器在并发场景调优
一.序言 目前企业级主流使用的Java版本是8,垃圾回收器支持手动修改为G1,G1垃圾回收器是Java 11的默认设置,因此G1垃圾回收器可以用很长时间,现阶段垃圾回收器优化意味着针对G1垃圾回收器优 ...
- Java GC系列(3):垃圾回收器种类
本文由 ImportNew - 好好先生 翻译自 javapapers. 目录 垃圾回收介绍 垃圾回收是如何工作的? 垃圾回收的类别 垃圾回收监视和分析 在这篇教程中我们将学习几种现有的垃圾回收器.在 ...
- [译]Java垃圾回收器的类型
说明:这篇文章来翻译来自于Javapapers 的Types of Java Garbage Collectors 在这部分的教程中我们将讲到可使用的四种不同类型的Java垃圾回收器.垃圾回收是Jav ...
- Hotspot JVM垃圾回收器
前两篇<JVM入门——运行时数据区><JVM常见垃圾回收算法>所提到的实际上JVM规范以及常用的垃圾回收算法,具体的JVM实现实际上不止一种,有JRockit.J9等待,当然最 ...
- 一篇文章让你了解GC垃圾回收器
简单了解GC垃圾回收器 了解GC之前我们首先要了解GC是要做什么的?顾名思义回收垃圾,什么是垃圾呢? GC回收的垃圾主要指的是回收堆内存中的垃圾对象. 从根对象出发,所有被引用的对象,都是存活对象 其 ...
- JVM 专题二十:垃圾回收(四)垃圾回收器 (一)
1. GC分类与性能指标 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商.不同版本的JVM来实现.由于JDK的版本处于高速迭代过程中,因此Java发展至今已经产生了众多的GC版本.从不同角度分 ...
- 如何选择JVM垃圾回收器?
明确垃圾回收器组合 -XX:+UseSerialGC 年轻代和老年代都用串行收集器 -XX:+UseParNewGC 年轻代使用ParNew,老年代使用 Serial Old -XX:+UsePara ...
随机推荐
- boss直聘__zp_stoken__逆向
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 目标网站 aHR0cHM6 ...
- echarts的示例跟做出来的不一样
先给大家看下我做出来的和echarts官网做出来的 代码什么的都是一模一样但是颜色不一样 它字的颜色和柱状图颜色还一样不知道是不是脑子有猫病~ 上面是我做的 下面是官网的 主要是代码都是一样 我又不 ...
- github fork后对上游仓库的做rebase
想对上游仓库做更新同步 先添加上游仓库 git remote add upstream https://github.com/原始作者/原始仓库.git 其中这里的upstream 是一个命名,和 o ...
- 阅读mmdetection3d框架的源码探索其构建dataset的流程
在查看一些基于mmdetection3d构建的代码的时候,一开始会摸不着头脑,它的dataset到底是怎么构造的? 接下来就直接下载mmdetection3d这个仓库,然后去分析里面的代码. 可以看到 ...
- spark读取hive表,org.apache.spark.sql.AnalysisException: Unsupported data source type for direct query on files: hive;
异常出现:spark读取hive表时,spark.read.table(hive.test) hdp版本的spark默认的catalog是spark,配置项 metastore.catalog.def ...
- ISCSI配置与挂载
ISCSI介绍 iSCSI使用 TCP/IP 协议,来提供网络存储. 客户端挂载后,可以对其进行分区,进行格式化,就好像是安装在本机上的硬盘一样. 为了保证传输速率,通常采用光纤. 配置环境 Cent ...
- Linux Driver : i2c-gpio
# Linux Driver : i2c-gpio https://www.cnblogs.com/haoxing990/p/4718834.html https://blog.csdn.net/ji ...
- QT 使用相对路径读取.txt文件
QT可以使用QFile来读取.txt文件,具体代码实现如下: 1 #include <QCoreApplication> 2 #include <QString> 3 #inc ...
- debian11 使用podman搭建 nacos-server
前言 基于debian11 + podman 搭建 nacos-server 用于简单测试. nacos-server基于java,如果直接运行还要准备java环境,在docker/podman 镜像 ...
- 关于python3多线程和协程
以下内容部分由chatgpt生成,本文仅作为备忘和记录. asyncio.sleep 和 time.sleep 都是用于在 Python 中进行延迟操作的函数,但它们的工作方式和使用场景有一些不同. ...