JAVA 之 GC 一
Stop-the-world
Stop-the-world会在任何一种GC算法中发生。Stop-the-world意味着 JVM 因为要执行GC而停止了应用程序的执行。当Stop-the-world发生时,除了GC所需的线程以外,所有线程都处于等待状态,直到GC任务完成。GC优化很多时候就是指减少Stop-the-world发生的时间。
后续为HotSpot虚拟机
在Java程序中不能显式地分配和注销内存。有些人把相关的对象设置为null或者调用System.gc()来试图显式地清理内存。设置为null至少没什么坏处,但是调用System.gc()会显著地影响系统性能,必须彻底杜绝.
在Java中,开发人员无法直接在程序代码中清理内存,而是由垃圾回收器自动寻找不必要的垃圾对象,并且清理掉他们。垃圾回收器会在下面两种假设(hypotheses)成立的情况下被创建(称之为假设不如改为推测(suppositions)或者前提(preconditions))。
- 大多数对象会很快变得不可达
- 只有很少的由老对象(创建时间较长的对象)指向新生对象的引用
HotSpot虚拟机将其物理上划分为两个–新生代(young generation)和老年代(old generation)。
新生代(Young generation): 绝大多数最新被创建的对象会被分配到这里,由于大部分对象在创建后会很快变得不可到达,所以很多对象被创建在新生代,然后消失。对象从这个区域消失的过程我们称之为”minor GC“。
老年代(Old generation): 对象没有变得不可达,并且从新生代中存活下来,会被拷贝到这里。其所占用的空间要比新生代多。也正由于其相对较大的空间,发生在老年代上的GC要比新生代少得多。对象从老年代中消失的过程,我们称之为”major GC“(或者”full GC“)
持久代( permanent generation )也被称为方法区(method area)。他用来保存类常量以及字符串常量。因此,这个区域不是用来永久的存储那些从老年代存活下来的对象。这个区域也可能发生GC。并且发生在这个区域上的GC事件也会被算为major GC。
新生代的构成
为了更好地理解GC,我们现在来学习新生代,新生代是用来保存那些第一次被创建的对象,他可以被分为三个空间
- 一个伊甸园空间(Eden )
- 两个幸存者空间(Survivor )
比例大概为(8:1)
一共有三个空间,其中包含两个幸存者空间。每个空间的执行顺序如下:
- 绝大多数刚刚被创建的对象会存放在伊甸园空间。
- 在伊甸园空间执行了第一次GC之后,存活的对象被移动到其中一个幸存者空间。
- 此后,在伊甸园空间执行GC之后,存活的对象会被堆积在同一个幸存者空间。
- 当一个幸存者空间饱和,还在存活的对象会被移动到另一个幸存者空间。之后会清空已经饱和的那个幸存者空间。
- 在以上的步骤中重复几次依然存活的对象,就会被移动到老年代。
如果你仔细观察这些步骤就会发现,其中一个幸存者空间必须保持是空的。
老年代GC处理机制
老年代空间的GC事件基本上是在空间已满时发生,执行的过程根据GC类型不同而不同,因此,了解不同的GC类型将有助于你理解本节的内容。
JDK7一共有5种GC类型:
- Serial GC
- Parallel GC
- Parallel Old GC (Parallel Compacting GC)
- Concurrent Mark & Sweep GC (or “CMS”)
- Garbage First (G1) GC
其中,Serial GC不应该被用在服务器上。这种GC类型在单核CPU的桌面电脑时代就存在了。使用Serial GC会显著的降低应用的性能指标。
1. Serial GC (-XX:+UseSerialGC)
老年代空间中的GC采取称之为”mark-sweep-compact“的算法。
- 算法的第一步是标记老年代中依然存活对象。(标记)
- 第二步,从头开始检查堆内存空间,并且只留下依然幸存的对象。(清理)
最后一步,从头开始,顺序地填满堆内存空间,并且将对内存空间分成两部分:一个保存着对象,另一个空着(压缩)。
2. Parallel GC (-XX:+UseParallelGC)
serial GC只使用一个线程执行GC,而parallel GC使用多个线程,因此parallel GC更高效。这种GC在内存充足以及多核的情况下会很有用,因此我们也称之为”throughput GC“。
3. Parallel Old GC(-XX:+UseParallelOldGC)
Parallel Old GC分为三步:标记-汇总-压缩(mark – summary – compaction)。汇总(summary)步骤与清理(sweep)的不同之处在于,其将依然幸存的对象分发到GC预先处理好的不同区域,算法相对清理来说略微复杂一点。
4. CMS GC (-XX:+UseConcMarkSweepGC)
第一步初始化标记(initial mark) 比较简单。这一步骤只是查找那些距离类加载器最近的幸存对象。因此,停顿的时间非常短暂。在之后的并行标记( concurrent mark )步骤,所有被幸存对象引用的对象会被确认是否已经被追踪和校验。这一步的不同之处在于,在标记的过程中,其他的线程依然在执行。在重新标记(remark)步骤,会再次检查那些在并行标记步骤中增加或者删除的与幸存对象引用的对象。最后,在并行交换( concurrent sweep )步骤,转交垃圾回收过程处理。垃圾回收工作会在其他线程的执行过程中展开。一旦采取了这种GC类型,由GC导致的暂停时间会极其短暂。CMS GC也被称为低延迟GC。它经常被用在那些对于响应时间要求十分苛刻的应用之上。
当然,这种GC类型在拥有stop-the-world时间很短的优点的同时,也有如下缺点:
- 它会比其他GC类型占用更多的内存和CPU
- 默认情况下不支持压缩步骤
在使用这个GC类型之前你需要慎重考虑。如果因为内存碎片过多而导致压缩任务不得不执行,那么stop-the-world的时间要比其他任何GC类型都长,你需要考虑压缩任务的发生频率以及执行时间。
5. G1 GC
注:GC的开销通常很大,而且它的运行具有不确定性,微软的编程规范里是强烈建议你不要显式调用GC。但你的代码中还是可以使用framework中GC的某些方法进行手动回收,前提是你必须要深刻理解GC的回收原理,否则手动调用GC在特定场景下很容易干扰到GC的正常回收甚至引入不可预知的错误。
Eclipse GC日志打印
在Eclipse.ini加-verbose:gc
-Xloggc:E:\GClog\gc.log
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails-XX:+PrintHeapAtGC-XX:+PrintGCDateStamps (from JDK 6 update 4)
842.467: [Full GC [PSYoungGen: 576K->0K(265728K)] [ParOldGen: 275707K->275900K(388608K)] 276283K->275900K(654336K) [PSPermGen: 130853K->130853K(182784K)], 0.6767182 secs] [Times: user=1.56 sys=0.00, real=0.68 secs]
902.455: [GC [PSYoungGen: 11935K->672K(226816K)] 287836K->276572K(615424K), 0.0104957 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
902.466: [Full GC [PSYoungGen: 672K->0K(226816K)] [ParOldGen: 275900K->276282K(388608K)] 276572K->276282K(615424K) [PSPermGen: 133030K->133029K(177664K)], 0.5321486 secs] [Times: user=1.62 sys=0.01, real=0.53 secs]
975.834: [GC [PSYoungGen: 225792K->13615K(250368K)] 502074K->289897K(638976K), 0.0142747 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
1466.749: [GC [PSYoungGen: 86064K->2654K(215552K)] 362346K->288833K(604160K), 0.0528907 secs] [Times: user=0.14 sys=0.00, real=0.05 secs]
1466.802: [Full GC [PSYoungGen: 2654K->0K(215552K)] [ParOldGen: 286179K->197300K(354816K)] 288833K->197300K(570368K) [PSPermGen: 262143K->139027K(259584K)], 0.8853333 secs] [Times: user=2.06 sys=0.02, real=0.88 secs]
1870.452: [GC [PSYoungGen: 212480K->3950K(231936K)] 409780K->201251K(586752K), 0.0286043 secs] [Times: user=0.09 sys=0.00, real=0.03 secs]
2631.725: [GC [PSYoungGen: 210286K->2048K(202240K)] 407587K->202547K(557056K), 0.0225240 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
3336.501: [GC [PSYoungGen: 202240K->2144K(218112K)] 402739K->203955K(572928K), 0.0190907 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
4050.503: [GC [PSYoungGen: 196704K->3488K(192512K)] 398515K->205299K(547328K), 0.0188174 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
4755.967: [GC [PSYoungGen: 192416K->4576K(203264K)] 394227K->206387K(558080K), 0.0208226 secs] [Times: user=0.08 sys=0.00, real=0.02 secs]
5409.812: [GC [PSYoungGen: 188384K->6827K(185856K)] 390195K->208638K(540672K), 0.0135900 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
6050.587: [GC [PSYoungGen: 185515K->7051K(191488K)] 387326K->208862K(546304K), 0.0234516 secs] [Times: user=0.08 sys=0.00, real=0.02 secs]
6680.430: [GC [PSYoungGen: 181131K->7979K(177664K)] 382942K->209790K(532480K), 0.0244785 secs] [Times: user=0.08 sys=0.00, real=0.02 secs]
7297.651: [GC [PSYoungGen: 177451K->9003K(181760K)] 379262K->210814K(536576K), 0.0196988 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
7853.957: [GC [PSYoungGen: 173867K->15813K(176640K)] 375678K->217624K(531456K), 0.0232200 secs] [Times: user=0.06 sys=0.00, real=0.02 secs]
7892.117: [GC [PSYoungGen: 176581K->24054K(178688K)] 378392K->229934K(533504K), 0.0272496 secs] [Times: user=0.08 sys=0.00, real=0.03 secs] GC日志说明:
GC打印时间: [垃圾回收类型回收时间: [收集器名称: 年轻代回收前占用大小->年轻代回收后占用大小(年轻代当前容量),
年轻代局部GC时JVM暂停处理的时间] 堆空间GC前占用的空间->堆空间GC后占用的空间(堆空间当前容量)
,GC过程中JVM暂停处理的时间]。
垃圾回收类型:分为GC和Full GC.
GC一般为堆空间某个区发生了垃圾回收,
Full GC基本都是整个堆空间及持久代发生了垃圾回收,通常优化的目标之一是尽量减少GC和Full GC的频率。
收集器名称:一般都为收集器的简称或别名,通过收集器名称基本都能判断出那个区发生了GC。
DefNew:年轻代(新生代)发生了GC (若为DefNew可知当前JVM年轻代使用的串行收集器)
ParNew:年轻代(新生代)发生了GC (若为ParNew可知当前JVM年轻代使用了并行收集器)
Tenured:老年代发生了GC
Perm:持久代发生了GC
转载于:https://www.cnblogs.com/binbang/p/6392845.html
JAVA 之 GC 一相关推荐
- Jvm 系列(六):Java 服务 GC 参数调优案例
本文介绍了一次生产环境的JVM GC相关参数的调优过程,通过参数的调整避免了GC卡顿对JAVA服务成功率的影响. 这段时间在整理jvm系列的文章,无意中发现本文,作者思路清晰通过步步分析最终解决问题. ...
- java中gc是啥_java的gc是什么
一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能提高整个应用程序 ...
- java gc 可以对方法区进行回收_浅谈 Java 之 GC
阅读本文假设你对java内存模型已有一些了解. 1.Java虚拟机中哪些内存需要回收? 先来看看jvm内存模型,如下图 线程隔离的区域随线程而生,随线程而灭:程序计数器可保存着虚拟机字节码指令的地址( ...
- gc java root_聊聊Java的GC机制
原标题:聊聊Java的GC机制 近日,MIUI在小米全球社区发布公告,表示MIUI将在全球市场销售的手机中预装谷歌拨号及谷歌消息应用程序(中国.印度.印度尼西亚等市场除外).小米表示,小米9T Pro ...
- Java的GC机制及算法
转载自 Java的GC机制及算法 GC的阶段 对每个对象而言,垃圾回收分为两个阶段:finalization和reclamation. finalization: 指运行这个对象的finaliz ...
- java超出gc开销_通过这5个简单的技巧减少GC开销
java超出gc开销 编写代码的五种简单方法,可以提高内存效率,而无需花费更多时间或降低代码可读性 垃圾回收会为您的应用程序增加多少开销? 您可能不知道确切的数字,但您确实知道总有改进的余地. 尽管自 ...
- java 打开gc日志_在运行时打开GC日志记录
java 打开gc日志 总是有下一个JVM表现不佳. 而且,您内心深知,如果您只有少数启动选项可以公开一些有关正在发生的事情的信息,那么您可能就有机会真正修复该死的东西. 但是不,您需要的标志( -X ...
- java超出gc开销限制_超出了GC开销限制– Java堆分析
java超出gc开销限制 这篇文章是我们原来的GC开销超出问题模式的延续. 正确的Java堆分析对于消除OutOfMemoryError:GC开销问题至关重要. 如果您不熟悉此Java HotSp ...
- Java服务GC参数调优案例
这段时间在整理jvm系列的文章,无意中发现本文,作者思路清晰通过步步分析最终解决问题.我个人特别喜欢这种实战类的内容,经原作者的授权同意,将文章分享于此.原文链接:Java服务GC参数调优案例,下面为 ...
- java的gc策略_Java的GC与内存分配策略
资料整理来源以及参考: Java的GC机制主要针对于 堆以及方法区 而言,对于程序计数器,虚拟机栈,本地方法栈三个区域是随着线程而生,随线程而灭的,栈中的栈帧随着方法的进入和退出有条不紊的执行出栈和入 ...
最新文章
- unity中摄像机的控制---调整摄像机,不让他摔倒
- R语言ggplot2可视化气泡图:无填充色的气泡图、自定义填充色的气泡图
- 网络工程师应该掌握的50个路由器知识要点
- 关于程序中的操作符左移和右移问题
- ADC内设与外设的区别
- 【DRP】【SQL】-悲观锁-防止多用户同时操作时出现脏数据
- 从陪练到赢家:人机博弈的六十年
- 暑期旅游超十亿人次,张家界成为大黑马,挤掉三亚西安排全国第三
- Pandas的crosstab函数
- matlab将z域变为s域,时域、S域、Z域转换
- Dot.js中添加函数用法
- ZStack-ZCCE-网络实验-VPC网络创建
- 键盘上哪个键是ESCAPE键?
- python参考手册 第一章
- Entity Framework使用DBContext实现增删改查示例
- 用Python实现单词尾缀的分析及提取
- Win系统集成一键显示隐藏系统文件到鼠标右键菜单.bat
- Pytorch阅读文档之flatten函数
- 面试积累-MySQL-MySQL中varchar与char的区别以及varchar(50)中的50代表的涵义?
- hashlib hamc