java 程序执行后 强制gc_GC 设计与停顿

(给ImportNew加星标,提高Java技能)

编译:唐尤华

链接:shipilev.net/jvm/anatomy-quarks/3-gc-design-and-pauses/

1. 写在前面

“[JVM 解剖公园][1]”是一个持续更新的系列迷你博客,阅读每篇文章一般需要5到10分钟。限于篇幅,仅对某个主题按照问题、测试、基准程序、观察结果深入讲解。因此,这里的数据和讨论可以当轶事看,不做写作风格、句法和语义错误、重复或一致性检查。如果选择采信文中内容,风险自负。

Aleksey Shipilёv,JVM 性能极客 

推特 [@shipilev][2]  

问题、评论、建议发送到 [aleksey@shipilev.net][3]

[1]:https://shipilev.net/jvm-anatomy-park

[2]:http://twitter.com/shipilev

[3]:aleksey@shipilev.net

 2. 问题

如果说垃圾回收是敌人,那么绝不能害怕,因为恐惧会让人逐步死去直至彻底消亡。等等,这里究竟要讨论什么问题?好吧,这里要讨论的是,“在 `ArrayList` 中分配1亿个对象会让 Java ‘打嗝’“ 是真的吗?

 3. 全貌图

可以简单地把性能问题归罪于通用 GC,而真正的问题是对于实际工作负载 GC 的表现没有达到预期。很多时候是工作负载本身有问题,其他情况则是使用了不匹配的 GC。请注意大多数回收器在其 GC 周期中是如何停顿的。

4. 实验

对于“向 `ArrayList` 加入1亿个对象”这个实验,虽然不切实际且略显搞笑,但在还是可以运行一下看看效果。下面是实验代码:

```java
import java.util.*;

public class AL {
static List l;public static void main(String... args) {
l = new ArrayList<>();for (int c = 0; c < 100_000_000; c++) {
l.add(new Object());
}
}
}
```

下面是来自奶牛的评论:

```shell
$ cowsay ...
________________________________________
/ 顺便说一下,这是一个糟糕的 GC 基准测试 \
| 即使我是一头奶牛,也能清楚地知道 |
\ 这一点。 /
----------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
```

尽管如此,即使一个糟糕的基准测试,只要仔细分析还是可以从运行结果中了解一些测试系统的有用信息。事实证明,在 OpenJDK 中选择不同的回收器及其对应的 GC 设计,在这样的负载下运行更能凸显彼此之间的差异。

下面使用 JDK 9 + Shenandoah 垃圾回收器享受 GC 所有最新改进。在配置较低的 1.7 GHz i5 超极本运行 Linux x86_64 进行测试。要分配1亿个16字节大小的对象,这里 heap 设为静态 4GB 以消除不同回收器之间的自由度差异。

4.1 G1(JDK9 默认 GC)

```shell
$ time java -Xms4G -Xmx4G -Xlog:gc AL
[0.030s][info][gc] Using G1
[1.525s][info][gc] GC(0) Pause Young (G1 Evacuation Pause) 370M->367M(4096M) 991.610ms
[2.808s][info][gc] GC(1) Pause Young (G1 Evacuation Pause) 745M->747M(4096M) 928.510ms
[3.918s][info][gc] GC(2) Pause Young (G1 Evacuation Pause) 1105M->1107M(4096M) 764.967ms
[5.061s][info][gc] GC(3) Pause Young (G1 Evacuation Pause) 1553M->1555M(4096M) 601.680ms
[5.835s][info][gc] GC(4) Pause Young (G1 Evacuation Pause) 1733M->1735M(4096M) 465.216ms
[6.459s][info][gc] GC(5) Pause Initial Mark (G1 Humongous Allocation) 1894M->1897M(4096M) 398.453ms
[6.459s][info][gc] GC(6) Concurrent Cycle
[7.790s][info][gc] GC(7) Pause Young (G1 Evacuation Pause) 2477M->2478M(4096M) 472.079ms
[8.524s][info][gc] GC(8) Pause Young (G1 Evacuation Pause) 2656M->2659M(4096M) 434.435ms
[11.104s][info][gc] GC(6) Pause Remark 2761M->2761M(4096M) 1.020ms
[11.979s][info][gc] GC(6) Pause Cleanup 2761M->2215M(4096M) 2.446ms
[11.988s][info][gc] GC(6) Concurrent Cycle 5529.427ms
real 0m12.016s
user 0m34.588s
sys 0m0.964s
```

从 G1 运行结果中能观察到什么?年轻代的停顿时间从500至1000毫秒不等。到达稳定状态后停顿开始减少,启发式方法给出了结束停顿需回收多少内存。一段时间后,会进入并发 GC 阶段直到结束(请注意年轻代与并发阶段重叠)。接下来应该还有“混合停顿”,但是 VM 已经提前退出。这些不确定的停顿是运行时间过长的罪魁祸首。

另外,可以注意到“user”时间比“real”(时钟时间)要长。由于 GC 并行执行,而应用程序是单线程执行,因此 GC 会利用所有可用的并行机制从而让收集时间变得比时钟时间短。

4.2 Parallel

```shell
$ time java -XX:+UseParallelOldGC -Xms4G -Xmx4G -Xlog:gc AL
[0.023s][info][gc] Using Parallel
[1.579s][info][gc] GC(0) Pause Young (Allocation Failure) 878M->714M(3925M) 1144.518ms
[3.619s][info][gc] GC(1) Pause Young (Allocation Failure) 1738M->1442M(3925M) 1739.009ms
real 0m3.882s
user 0m11.032s
sys 0m1.516s
```

从 Parallel 结果中,可以看到类似的年轻代停顿。原因可能是调整 Eden 区或 Survivor 区的大小以容纳更多临时分配的内存。这里有两次长停顿,完成任务总用时很短。当处于稳定状态,回收器会保持相同频率的长停顿。“user”时间同样远大于“real”时间,并发隐藏了一些 GC 开销。

4.3 CMS(并发标记-清扫)

```shell
$ time java -XX:+UseConcMarkSweepGC -Xms4G -Xmx4G -Xlog:gc AL
[0.012s][info][gc] Using Concurrent Mark Sweep
[1.984s][info][gc] GC(0) Pause Young (Allocation Failure) 259M->231M(4062M) 1788.983ms
[2.938s][info][gc] GC(1) Pause Young (Allocation Failure) 497M->511M(4062M) 871.435ms
[3.970s][info][gc] GC(2) Pause Young (Allocation Failure) 777M->850M(4062M) 949.590ms
[4.779s][info][gc] GC(3) Pause Young (Allocation Failure) 1117M->1161M(4062M) 732.888ms
[6.604s][info][gc] GC(4) Pause Young (Allocation Failure) 1694M->1964M(4062M) 1662.255ms
[6.619s][info][gc] GC(5) Pause Initial Mark 1969M->1969M(4062M) 14.831ms
[6.619s][info][gc] GC(5) Concurrent Mark
[8.373s][info][gc] GC(6) Pause Young (Allocation Failure) 2230M->2365M(4062M) 1656.866ms
[10.397s][info][gc] GC(7) Pause Young (Allocation Failure) 3032M->3167M(4062M) 1761.868ms
[16.323s][info][gc] GC(5) Concurrent Mark 9704.075ms
[16.323s][info][gc] GC(5) Concurrent Preclean
[16.365s][info][gc] GC(5) Concurrent Preclean 41.998ms
[16.365s][info][gc] GC(5) Concurrent Abortable Preclean
[16.365s][info][gc] GC(5) Concurrent Abortable Preclean 0.022ms
[16.478s][info][gc] GC(5) Pause Remark 3390M->3390M(4062M) 113.598ms
[16.479s][info][gc] GC(5) Concurrent Sweep
[17.696s][info][gc] GC(5) Concurrent Sweep 1217.415ms
[17.696s][info][gc] GC(5) Concurrent Reset
[17.701s][info][gc] GC(5) Concurrent Reset 5.439ms
real 0m17.719s
user 0m45.692s
sys 0m0.588s
```

与一般看法相反,CMS 中的 “Concurrent”指年老代并发回收。正如结果中看到的,年轻代还是处于万物静止状态。GC 日志看起来与 G1 类似:年轻代暂停,循环进行并发收集。区别在于,与 G1 “混合停顿”相比,“并发清扫”可以不间断清扫不会造成应用停止。年轻代 GC 停顿时间越长影响了任务的执行性能。

4.4 Shenandoah

```shell
$ time java -XX:+UseShenandoahGC -Xms4G -Xmx4G -Xlog:gc AL
[0.026s][info][gc] Using Shenandoah
[0.808s][info][gc] GC(0) Pause Init Mark 0.839ms
[1.883s][info][gc] GC(0) Concurrent marking 2076M->3326M(4096M) 1074.924ms
[1.893s][info][gc] GC(0) Pause Final Mark 3326M->2784M(4096M) 10.240ms
[1.894s][info][gc] GC(0) Concurrent evacuation 2786M->2792M(4096M) 0.759ms
[1.894s][info][gc] GC(0) Concurrent reset bitmaps 0.153ms
[1.895s][info][gc] GC(1) Pause Init Mark 0.920ms
[1.998s][info][gc] Cancelling concurrent GC: Stopping VM
[2.000s][info][gc] GC(1) Concurrent marking 2794M->2982M(4096M) 104.697ms

real 0m2.021s
user 0m5.172s
sys 0m0.420s
```

[Shenandoah][4] 回收器中没有年轻代,至少今天如此。也有一些不引入分代进行部分回收的设想,但几乎不可能避免万物静止的情况。并发 GC 与应用同步启动,初始化标记和结束并发标记引发了两次小停顿。因为所有内容都处于活跃状态没有碎片化,所以并发拷贝不会引发停顿。第二次 GC 由于 VM 关闭过早结束了。由于没有其它回收器那样的长停顿,任务很快执行结束。

[4]:https://wiki.openjdk.java.net/display/shenandoah/Main

4.5 Epsilon

```shell
$ time java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xms4G -Xmx4G -Xlog:gc AL
[0.031s][info][gc] Initialized with 4096M non-resizable heap.
[0.031s][info][gc] Using Epsilon GC
[1.361s][info][gc] Total allocated: 2834042 KB.
[1.361s][info][gc] Average allocation rate: 2081990 KB/sec

real 0m1.415s
user 0m1.240s
sys 0m0.304s
```

使用实验性“no-op” [Epsilon GC][5] 不会运行任何回收器,有助于评估 GC 开销。 我们可以准确地放入预先设定好的 4GB 堆,应用运行过程中没有任何停顿。不过,发生任何突然的变化都导致程序结束。注意,“real”和“user” + “sys”的时间几乎相等,这证实了应用只有一个线程。

*译注:Epsilon GC 处理内存分配,但不实现任何实际的内存回收机制。一旦可用的Java堆耗尽,JVM就会关闭。*

[5]:http://openjdk.java.net/jeps/318

5. 观察

不同的 GC 实现有着各自的设计权衡,取消 GC 可看作一种延伸的“坏主意”。通过了解工作负载、性能要求以及可用的 GC 实现,才能根据实际情况选择合适的回收器。即使选择不使用 GC 的目标平台,仍然需要知道并选择本机内存分配器。当运行实验负载时,请试着理解运行结果并从中学习。祝你好运!

推荐阅读

(点击标题可跳转阅读)

JDK 11 将引入低延迟 GC,大幅度缩短 GC 暂停时长

减少 GC 开销的 5 个编码技巧

杂谈 GC

看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

4f9e81d417368eb565310032199bd7fc.png

喜欢就点一下「好看」呗~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/259157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

除BUG记

我负责一个模块&#xff0c;功能比较简单&#xff0c;就是测量环境温、湿度&#xff0c;外加控制空调开/关、温度设定。就是这么几个功能&#xff0c;就反复试验、修改&#xff0c;才达到稳定。在调试时&#xff0c;出现各种各样的BUG&#xff0c;一些是编程时候出现的语法错误…

正则表达式语法(转)

正则表达式是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff09;和特殊字符&#xff08;称为“元字符”&#xff09;。模式描述在搜索文本时要匹配的一个或多个字符串。 正则表达式示例 表达式匹配 /^\s*$/ 匹配空行。 /\d{2}-…

迎战校招训练题

一、双空 编译器可以根据硬件特性选择合适的类型长度&#xff0c;但要遵循如下限制&#xff1a;short与int类型至少为___C___位&#xff0c;long至少为__D____位&#xff0c;并且short类型不长于int类型&#xff0c;int类型不得长于long类型。 A. 4 B.8 C.16 D. 32 E. 64…

【ASP.NET Web API2】初识Web API

Web Api 是什么&#xff1f; MSDN&#xff1a;ASP.NET Web API 是一种框架&#xff0c;用于轻松构建可以访问多种客户端&#xff08;包括浏览器和移动设备&#xff09;的 HTTP 服务 百度百科&#xff1a;Web API是网络应用程序接口。 个人理解&#xff1a;Web API 是提供给多种…

三星s8怎么分屏操作_三星手机该怎么玩?了解完这几点用机技巧,可以轻车熟路了!...

其实对于三星这个手机品牌&#xff0c;我还是很佩服的。虽然近些年来&#xff0c;三星在国内的市场份额日渐变少&#xff0c;但是在国内的影响力依然尚存。毕竟三星手机在某些方面还是很有优势的&#xff0c;特别是旗舰系列机型深受消费者喜爱。接下来&#xff0c;笔者就跟大家…

关于条件编译的问题

这两天来忙活ucos-II在PIC18fxxx系列上的移植。在编译的时候老出现变量被多重定义的错误。花费了一天的功夫才成功编译通过&#xff0c;错误何在&#xff1f;&#xff1f;就是因为没有搞明白条件编译的原理&#xff0c;二是对mcc18编译器的特点无知。下面学习条件编译方面的知识…

二维数组的指针复习

最近一次的考试都是指针&#xff0c;真是给我深深上了一课&#xff0c;所以我特此复习一下指针方面的知识。二维数组的指针 int a[3][4] {{1,3,5,7},{9,11,13,15},{17,19,21,23}}; 下面通过一个表来做详细的说明&#xff1a; 访问二维数组&#xff0c;有两种方法&#xff0c;一…

称重的问题

给你8颗小石头和一架托盘天平。有7颗石头的重量是一样的&#xff0c;另外一颗比其他石头略重&#xff1b;除此之外&#xff0c;这些石头完全没有分别。你不得假设那颗重头到底比其他的石头重了多少。请问&#xff1a;最少要称量几次&#xff0c;你才能把那颗较重的石头找出来&a…

TIF图像文件的读取(c++代码)

一 TIF图像介绍 TIFF是最复杂的一种位图文件格式。TIFF是基于标记的文件格式&#xff0c;它广泛地应用于对图像质量要求较高的图像的存储与转换。由于它的结构灵活和包容性大&#xff0c;它已成为图像文件格式的一种标准&#xff0c;绝大多数图像系统都支持这种格式。 TIFF 是一…

g menu i meun_长沙话读“这里”,到底是阁(gó)里还是该(gái)里

“带笼子”、“打抱秋”……这些地道的长沙话&#xff0c;长沙人&#xff0c;你有多久没听过了&#xff1f;/ 长沙人&#xff0c;你还记得长沙话吗 / “去了很多地方&#xff0c;最后还是回到了长沙”“我听见了一句长沙话&#xff0c;就想回长沙了。”逗霸妹听过很多人回长沙的…

git使用---工作区和暂存区

转载于:https://www.cnblogs.com/momo-unique/articles/4380551.html

UC/OS-II的学习

粗略的的看了邵贝贝老师的那本书&#xff0c;感觉有点眉目。UC/OS-II的全局变量繁多&#xff0c;刚接触的时候容易弄混淆&#xff0c;现在总结下&#xff1a; OSRunning&#xff1a; 用于标识多任务环境是否已经开启运行&#xff0c;在OSStart()函数里启动任务后就置为True。 …

偶数哥德巴赫猜想

已知不小于6的偶数都可以分成两个素数之和。请编写6到100000的所有偶数的分解&#xff0c;若有一个偶数可以分解成多个素数之和&#xff0c;只需写出一种即可。 #include <iostream> #include <algorithm> using namespace std;bool isprime(int n)//判断素数{int …

[20170420]表达式加0或者减0不一样.txt

[20170420]表达式加0或者减0不一样.txt --//oracle 有时候避免某个索引采用字段0或者-0的方式&#xff0c;不使用索引&#xff0c;但是两者存在一点点区别&#xff0c;通过例子说明。 1.环境&#xff1a; SCOTTbook> &r/ver1 PORT_STRING VERSION …

MAPLAP开发环境中release模式和debug模式烧写.hex文件的不同之处

昨天看了齐工的报告才知道release模式和debug模式烧写.hex文件的不同。 三&#xff1a;问题分析 1. PIC系列的仿真器和集成开发环境的情况&#xff1a; Release模式和Debug模式是有区别的&#xff1b;Release模式是只把代码烧录到单片机的flash区内&#xff0c;上电执行&am…

JavaWeb -- Session实例 -- 自动登录 和 防止表单重复提交(令牌产生器) MD5码

1、 自动登录 http://blog.csdn.net/xj626852095/article/details/16825659 2. 防止表单重复提交 表单Servlet //负责产生表单 public class FormServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletEx…

电脑常见故障处理_彩超常见故障及维修

彩超是医学检测手段中重要的环节之一&#xff0c;是对产妇以及对病人进行内部组织和结构观察的重要方式之一&#xff0c;彩超应用得当可以及早的诊断出病人的疾病&#xff0c;为患者解除疾病的困扰。彩超设备是一种极为先进的诊断系统&#xff0c;一般彩超系统包括以下几个部分…

微软历史最高市值是多少?

有人说微软在1999 年 12 月达到股价历史最高点 $58.38并不准确。我1999年12月22日增加微软&#xff0c;公司依照1999年12月27日的价格&#xff08;119.125&#xff0c;拆股后变为59.5625&#xff09;给了我一笔期权&#xff0c;这个价格&#xff0c;成为微软股价空前绝后最高点…

京东2016校招编程题

记得有一个大题&#xff0c;说的是给定一个n*n的矩阵&#xff0c;要求从1开始填充矩阵&#xff0c;最后的矩阵是蛇形的。即如下&#xff1a; n3, 7 8 1 6 9 2 5 4 3 n4, 10 11 12 1 9 16 13 2 8 15 14 3 7 6 5 4 给出代码&#xff1a; #incl…

leetcode21

/*** Definition for singly-linked list.* public class ListNode {* public int val;* public ListNode next;* public ListNode(int x) { val x; }* }*/ public class Solution {public ListNode MergeTwoLists(ListNode l1, ListNode l2) {//递归实现链表合并…