次要GC,主要GC与完整GC

在使用Plumbr中的GC暂停检测功能时,我被迫通过大量有关该主题的文章,书籍和演示工作。 在整个旅程中,我多次对次要,主要和完全GC事件的使用(误用)感到困惑。 这导致了这篇博客文章,我希望我设法消除一些困惑。

该帖子希望读者熟悉JVM中内置的通用垃圾收集原理。 将堆划分为Eden,幸存者和终身/旧空间,世代假设和不同的GC算法不在本文范围之内。

次要gc主要gc完整gc

次要GC

从年轻空间(由Eden和Survivor空间组成)收集垃圾称为次要GC 。 这个定义既清晰又统一。 但是,在处理次要垃圾回收事件时,您仍然应该注意一些有趣的注意事项:

  1. 当JVM无法为新对象分配空间时(例如,伊甸园已满),总是会触发次要GC。 因此,分配率越高,次要GC的执行频率就越高。
  2. 只要池已满,就将复制其全部内容,并且指针可以再次从零开始跟踪可用内存。 因此,代替经典的Mark,Sweep和Compact,使用Mark and Copy来清理Eden和Survivor空间。 因此,在伊甸园或幸存者空间内实际上没有发生碎片。 写指针始终位于使用的池的顶部。
  3. 在次要GC事件中,有效生成将被忽略。 从终身代到年轻代的参考实际上被认为是GC的根源。 在标记阶段,从年轻一代到终身代的引用都将被忽略。
  4. 与通常的看法相反,所有次要GC都会触发世界暂停 ,从而停止应用程序线程。 对于大多数应用程序,暂停的长度在延迟方面可以忽略不计。 如果可以将Eden中的大多数对象视为垃圾,并且永远不要将其复制到Survivor / Old空间,则为true。 如果相反的情况是正确的,并且大多数新生对象都不符合GC的条件,则次要GC暂停将开始花费更多时间。

因此,对于Minor GC而言,情况非常清楚- 每个Minor GC都会清洗年轻一代

主GC与全GC

应该注意的是,这些术语没有正式的定义。 JVM规范和垃圾收集研究论文中都没有。 但乍看之下,在我们认为对次要GC清洁年轻空间的正确认识之上建立这些定义应该很简单:

  • 主要GC正在清理使用权空间。
  • Full GC正在清洁整个堆堆–年轻的和终身使用的空间。

不幸的是,它有点复杂和混乱。 首先-许多主要GC由次要GC触发,因此在很多情况下不可能将两者分开。 另一方面,许多现代垃圾收集仅对部分使用权空间进行清理,因此,再次使用术语“清理”仅部分正确。

这就引出了一个点,您不必担心GC是被称为Major GC还是Full GC,而应专注于确定当前的GC是停止了所有应用程序线程还是能够与应用程序线程同时进行

JVM标准工具中甚至内置了这种混淆。 我的意思最好通过一个例子来解释。 让我们比较在运行并发标记和清除收集器( -XX:+ UseConcMarkSweepGC )的JVM上跟踪GC的两种不同工具的输出

首先尝试通过jstat输出获得洞察力:

my-precious: me$ jstat -gc -t 4235 1s
Time S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   5.7 34048.0 34048.0  0.0   34048.0 272640.0 194699.7 1756416.0   181419.9  18304.0 17865.1 2688.0 2497.6      3    0.275   0      0.000    0.2756.7 34048.0 34048.0 34048.0  0.0   272640.0 247555.4 1756416.0   263447.9  18816.0 18123.3 2688.0 2523.1      4    0.359   0      0.000    0.3597.7 34048.0 34048.0  0.0   34048.0 272640.0 257729.3 1756416.0   345109.8  19072.0 18396.6 2688.0 2550.3      5    0.451   0      0.000    0.4518.7 34048.0 34048.0 34048.0 34048.0 272640.0 272640.0 1756416.0  444982.5  19456.0 18681.3 2816.0 2575.8      7    0.550   0      0.000    0.5509.7 34048.0 34048.0 34046.7  0.0   272640.0 16777.0  1756416.0   587906.3  20096.0 19235.1 2944.0 2631.8      8    0.720   0      0.000    0.720
10.7 34048.0 34048.0  0.0   34046.2 272640.0 80171.6  1756416.0   664913.4  20352.0 19495.9 2944.0 2657.4      9    0.810   0      0.000    0.810
11.7 34048.0 34048.0 34048.0  0.0   272640.0 129480.8 1756416.0   745100.2  20608.0 19704.5 2944.0 2678.4     10    0.896   0      0.000    0.896
12.7 34048.0 34048.0  0.0   34046.6 272640.0 164070.7 1756416.0   822073.7  20992.0 19937.1 3072.0 2702.8     11    0.978   0      0.000    0.978
13.7 34048.0 34048.0 34048.0  0.0   272640.0 211949.9 1756416.0   897364.4  21248.0 20179.6 3072.0 2728.1     12    1.087   1      0.004    1.091
14.7 34048.0 34048.0  0.0   34047.1 272640.0 245801.5 1756416.0   597362.6  21504.0 20390.6 3072.0 2750.3     13    1.183   2      0.050    1.233
15.7 34048.0 34048.0  0.0   34048.0 272640.0 21474.1  1756416.0   757347.0  22012.0 20792.0 3200.0 2791.0     15    1.336   2      0.050    1.386
16.7 34048.0 34048.0 34047.0  0.0   272640.0 48378.0  1756416.0   838594.4  22268.0 21003.5 3200.0 2813.2     16    1.433   2      0.050    1.484

此片段是从JVM启动后的前17秒中提取的。 根据此信息,我们可以得出结论,在12次次要GC运行之后,执行了两次完整GC运行,总共运行了50毫秒 。 您将通过基于GUI的工具(例如jconsole或jvisualvm)得到相同的确认。

在点此结论之前,让我们看一下从同一JVM启动收集的垃圾收集日志的输出。 显然-XX:+ PrintGCDetails告诉了我们一个不同而更详细的故事:

java -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC eu.plumbr.demo.GarbageProducer
3.157: [GC (Allocation Failure) 3.157: [ParNew: 272640K->34048K(306688K), 0.0844702 secs] 272640K->69574K(2063104K), 0.0845560 secs] [Times: user=0.23 sys=0.03, real=0.09 secs] 
4.092: [GC (Allocation Failure) 4.092: [ParNew: 306688K->34048K(306688K), 0.1013723 secs] 342214K->136584K(2063104K), 0.1014307 secs] [Times: user=0.25 sys=0.05, real=0.10 secs] 
... cut for brevity ...
11.292: [GC (Allocation Failure) 11.292: [ParNew: 306686K->34048K(306688K), 0.0857219 secs] 971599K->779148K(2063104K), 0.0857875 secs] [Times: user=0.26 sys=0.04, real=0.09 secs] 
12.140: [GC (Allocation Failure) 12.140: [ParNew: 306688K->34046K(306688K), 0.0821774 secs] 1051788K->856120K(2063104K), 0.0822400 secs] [Times: user=0.25 sys=0.03, real=0.08 secs] 
12.989: [GC (Allocation Failure) 12.989: [ParNew: 306686K->34048K(306688K), 0.1086667 secs] 1128760K->931412K(2063104K), 0.1087416 secs] [Times: user=0.24 sys=0.04, real=0.11 secs] 
13.098: [GC (CMS Initial Mark) [1 CMS-initial-mark: 897364K(1756416K)] 936667K(2063104K), 0.0041705 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
13.102: [CMS-concurrent-mark-start]
13.341: [CMS-concurrent-mark: 0.238/0.238 secs] [Times: user=0.36 sys=0.01, real=0.24 secs] 
13.341: [CMS-concurrent-preclean-start]
13.350: [CMS-concurrent-preclean: 0.009/0.009 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
13.350: [CMS-concurrent-abortable-preclean-start]
13.878: [GC (Allocation Failure) 13.878: [ParNew: 306688K->34047K(306688K), 0.0960456 secs] 1204052K->1010638K(2063104K), 0.0961542 secs] [Times: user=0.29 sys=0.04, real=0.09 secs] 
14.366: [CMS-concurrent-abortable-preclean: 0.917/1.016 secs] [Times: user=2.22 sys=0.07, real=1.01 secs] 
14.366: [GC (CMS Final Remark) [YG occupancy: 182593 K (306688 K)]14.366: [Rescan (parallel) , 0.0291598 secs]14.395: [weak refs processing, 0.0000232 secs]14.395: [class unloading, 0.0117661 secs]14.407: [scrub symbol table, 0.0015323 secs]14.409: [scrub string table, 0.0003221 secs][1 CMS-remark: 976591K(1756416K)] 1159184K(2063104K), 0.0462010 secs] [Times: user=0.14 sys=0.00, real=0.05 secs] 
14.412: [CMS-concurrent-sweep-start]
14.633: [CMS-concurrent-sweep: 0.221/0.221 secs] [Times: user=0.37 sys=0.00, real=0.22 secs] 
14.633: [CMS-concurrent-reset-start]
14.636: [CMS-concurrent-reset: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

根据这些信息,我们可以看到,确实在12次次要GC运行之后,“情况有所不同”开始发生。 但是实际上,这是两个完全GC运行,而实际上,这“不同的事情”只是一个在Tenured一代中运行的GC,它由不同的阶段组成:

  • 初始标记阶段,跨度为0.0041705秒或约4ms。 此阶段是一个世界停止事件,它将停止所有应用程序线程以进行初始标记。
  • 同时执行Markup和Preclean阶段。 这些与应用程序线程并发运行
  • 最终备注阶段,范围为0.0462010秒或大约46ms。 此阶段再次是世界停止事件。
  • 并发执行Sweep操作。 顾名思义,此阶段也可以并发执行,而无需停止应用程序线程。

因此,我们从实际的垃圾收集日志中看到的是–代替了两次Full GC操作,实际上只执行了一次Main GC Cleaning Old空间。

如果您在等待时间之后,那么根据jstat揭示的数据做出决策将使您朝着正确的决策方向迈进。 它正确地列出了两个总计为50ms的世界停止事件,这些事件当时影响了所有活动线程的延迟。 但是,如果您尝试针对吞吐量进行优化,那么您可能会被误导-仅列出世界末日的初始标记和最终标记阶段,而jstat输出将完全隐藏正在完成的并发工作。

结论

考虑到这种情况,最好甚至避免以次要,主要或完全GC的方式进行思考。 而是监视您的应用程序的延迟或吞吐量,并将GC事件链接到结果。 除了这些事件之外,您还需要了解特定的GC事件是强制所有应用程序线程停止还是是并发处理的事件的一部分。

如果您喜欢这些内容-这是我们垃圾收集手册中的示例章节。 整个手册将于2015年3月发行。

翻译自: https://www.javacodegeeks.com/2015/03/minor-gc-vs-major-gc-vs-full-gc.html

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

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

相关文章

怎么改字段名称_精装房这么改!换门框,封阳台,效果出来比毛坯房还好

最近有朋友后台留言,称自己原本想一步到位购买精装房,但收房验收时才发现根本不合心意,空间利用率太低配色也老气,已经打算重新改装了。所谓精装房就是开发商将地板、门、厨房、卫生间、少量天花和部分柜子进行安装,业主只要添加些…

ZF2系列 – Zend Framework 2 MVC實作 (Part 3)

我之所以這麼喜歡Zend Framework的關係,其中一項就是它可以幫你很簡單的實現MVC的架構,所以今天的任務就是要建立一個以MVC為架構的基本網頁。 首先當然要先建立一個測試用的資料庫,因此我簡單的建立了一個名為Employee的資料表來進行展示&am…

方向盘左右能摇动_学车这么久了,你还不会打方向盘呢?

刚刚学车的朋友肯定有过这样的问题:教练说:往左打两圈,往右打一圈,往左打半圈……你乖乖听话照做,然后教练说:回正吧。是不是一下就蒙住了??我刚才打几圈来着?&#xff1…

Asp.Net基础 - 9.Web开发原则

目录: 9.1.Web开发的一些基本原则 9.2 原则一 9.2.1 C#代码是运行在服务器端的,JS代码是运行在浏览器客户端的 9.2.2 在服务器端“弹出消息窗口” 9.2.3 案例说明(客户端与服务端互不影响) 9.3 原则二 9.4 原则三 9.4.1 客户端…

重置手机_【轻松办税】ITS客户端申报密码重置不会操作?看过来,3分钟包你会!...

ITS扣缴客户端实名办税功能已经上线了,并且在2020年1月1日后将不再支持以CA方式进行登录,可是公司申报密码是很久前去大厅办理的,早就忘记了,这可咋好?别急,快来关注“上海税务”,包你3分钟学会…

Drools 6.2.0.Final发布

我们很高兴宣布最新,最出色的Drools 6.2.0.Final版本。 特别是此发行版更加注重改进的可用性和功能,这些功能使项目更易于使用(和采用)。 新功能包括对工作台UI的大量改进,对社交活动和插件管理的支持以及规则的全新E…

对OIM Web(UI)层进行压力测试

Oracle IDM中的默认配置保留20个专用于服务前端(UI)请求的线程 。 从根本上讲,这意味着应用程序服务器具有20个线程池,可用于为通过Web控制台(/ identity或/ sysadmin)访问OIM的用户提供服务。 对于Weblog…

java 拼接html_程序员用1.5小时写出的Java代码,让同事瞠目结舌!直呼优秀

1.曾经不止一次在生产中见过类似这样的代码:这有很多变种,例如用 Integer.valueOf(1)、 (Integer)1 之类的,那些细节都不重要。重要的是:凭空用一个 Integer 对象作为锁对象。2.AbstractComponentBuilderTemplateFactory3.HelloWo…

本地项目antd 修改.less文件导致内存溢出

项目场景: antd 项目,修改less文件会导致内存溢出 问题描述 本地环境antd 项目,修改less文件会导致内存溢出;如下 FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory原因分析: 这…

序列化代理模式

在上一篇文章中 ,我谈到了一般的序列化。 这是更加集中的内容,并提供了一个细节: 序列化代理模式 。 这是处理序列化许多问题的一种好方法,通常是最好的方法。 如果开发人员只想了解有关该主题的一件事,我会告诉他。 …

函数分组学通MongoDB——第三天 细说高级操作

改章节个人在广东喝咖啡的时候突然想到的...明天就有想写几篇关于函数分组的文章,所以回家到后之就奋笔疾书的写出来发布了 明天跟大家分享一下mongodb中比拟好玩的识知,要主括包:聚合,标游。 一: 聚合 见常的聚合作操…

eps如何建立立体白模_服装立体裁剪教程 结构都是“立裁”出来的 才智服装

核心提示:基础立裁服装立体裁剪是指用白坯布为常用替代物,在人台上直接塑造服装样式,并进行样板制作的技术。由于立体裁剪是设计师主要依靠视觉进行的直观操作的过程,所以它具有激发和展开新的设计思维的功能。一、基础立裁服装立…

平衡抽象原理

使代码复杂易读和理解的一件事是&#xff0c;方法内部的指令处于不同的抽象级别。 假设我们的应用程序仅允许登录用户查看其朋友的旅行。 如果用户不是朋友&#xff0c;则不会显示任何行程。 一个例子&#xff1a; public List<Trip> tripsByFriend(User user, User l…

IntelliJ IDEA内部设计

IntelliJ IDEA的第一版于2001年1月发布&#xff0c;当时它是第一个集成了高级代码导航和代码重构功能的Java IDE之一。 2009年&#xff0c;JetBrains开源了其社区版本 。 从那时起&#xff0c;创建了许多基于它的IDE&#xff0c;例如Google的Android Studio。 让我们使用JArc…

TDD:MS自带的单元测试 之 线程模型和执行顺序

背景 我一直在呼喊“不要靠假设编程”&#xff0c;可是我却常常这么做。我用单元测试就是一种基于假设进行编程的反面教材&#xff0c;今天就下决心弄明白它。 主要想弄明白两个问题&#xff1a; 执行的所有单元测试方法的线程模型&#xff0c;是单线程&#xff1f;是多线程&am…

bat文件名操作_Excel按文件名制作目录,你复制粘贴花一小时,同事只要十秒搞定...

Excel有个特殊操作&#xff0c;那就是对文件夹中上百个文件&#xff0c;用Excel按照文件名制作目录&#xff0c;我们只需要点击对于的超链接就可以快速打开对于的文件。如上图所示&#xff0c;我们的文件夹中包含有26个视频和Excel文件&#xff0c;因为文件数量过多所以我们需要…

整数返回poj1005——I Think I Need a Houseboat

这两天一直在研究整数返回之类的问题,上午正好有机会和大家讨论一下. 原题&#xff1a; Description Fred Mapper is considering purchasing some land in Louisiana to build his house on. In the process of investigating the land, he learned that the state of Louisia…

偷窥JCache API(JSR 107)

这篇文章从较高的层次介绍了JCache API&#xff0c;并提供了一个预告片–刚够您&#xff08;希望&#xff09;开始对此发痒&#xff1b;-) 在这篇文章中...。 JCache概述 JCache API&#xff0c;实现 JCache API支持的&#xff08;Java&#xff09;平台 快速了解Oracle Coh…

canvas 圆角矩形填充_一篇文章让你学会你最“害怕”的Canvas,太有意思了

Canvas画布 基本用法<canvas idcanvas width"150" height"150"></canvas> <canvas>看起来跟img标签有点像&#xff0c;唯一不同的是它没有src属性和alt属性。实际上&#xff0c;canvas标签只有两个属性:width和height。 如果没有设置宽度…

abaqus单位_ANSYS和ABAQUS哪个好,一个例子告诉你

分别用ANSYS和ABAQUS来分析同一个题目并考察其异同点。【问题】一根悬臂梁&#xff0c;长200mm,截面是30mm*20mm的矩形(高度方向是20mm)。该梁左端固定&#xff0c;在其上面施加向下的分布力系&#xff0c;载荷集度是0.6Mpa.已知材料使用低碳钢&#xff0c;弹性模量是200GPA&am…