垃圾收集算法,垃圾收集器
这些是我需要调整GC时通常会看到的一些准则和技巧。 主要由以下两本书组成,而根据我的经验却很少:
- Java性能
- JBoss AS 5性能调优
希望它们对在那里的其他人有用!
垃圾收集器
XX:+AggressiveOpts
将HotSpot内部布尔变量设置为true以启用其他性能优化
****************************************************** ******************************************************
由于大多数对象在其生命周期内最多只能被一个线程锁定,因此启用-XX:+ UseBiasedLocking可使该线程将锁定偏向自身。 一旦有偏见,该线程便可以随后锁定和解锁对象,而无需诉诸昂贵的原子指令
****************************************************** ******************************************************
要打印默认的人体工程学值:
java -XX:+PrintCommandLineFlags -version
****************************************************** ******************************************************
-XX:+PrintGCDetails
打印更多且更有价值的垃圾收集信息。
以下示例显示了通过-XX:+UseParallelGC
或-XX:+UseParallelOldGC
启用的Java 6 Update 25吞吐量垃圾收集器的-XX:+PrintGCDetails
输出示例。 输出分布在多行中,以便于阅读。
[GC
[PSYoungGen: 99952K->14688K(109312K)]
422212K->341136K(764672K), 0.0631991 secs]
[Times: user=0.83 sys=0.00, real=0.06 secs]
****************************************************** ******************************************************
包括日期和时间戳
-XX:+PrintGCTimeStamps
YYYY-MM-DD-T-HH-MM-SS.mmm-TZ
当-XX:+PrintGCDetails
与-Xloggc:<filename>
结合使用时,即使不指定-XX:+PrintGCTimeStamps
,输出也会自动带有时间戳作为前缀。
****************************************************** ******************************************************
[Full GC (System)
[PSYoungGen: 99608K->0K(114688K)]
[PSOldGen: 317110K->191711K(655360K)]
416718K->191711K(770048K)
[PSPermGen: 15639K->15639K(22528K)],
0.0279619 secs]
[Times: user=0.02 sys=0.00, real=0.02 secs]
系统意味着代码中有一个System.gc()
****************************************************** ******************************************************
http://sysadminsjourney.com/2008/09/15/profile-gc-with-gchisto/
****************************************************** ******************************************************
VisualVM和VisualGC插件
如果远程,则需要安装jstatd守护程序
****************************************************** ****************************************************
分层服务器运行时是通过-server -XX:+TieredCompilationcommand
行选项启用的。
小费
如果您不知道最初选择哪个运行时,请从服务器运行时开始。 如果无法满足启动时间或内存占用量要求,并且您正在使用Java 6 Update 25或更高版本,请尝试分层服务器运行时。 如果您没有运行Java 6 Update 25或更高版本,或者分层服务器运行时无法满足启动时间或内存占用量要求,请切换到客户端运行时。
****************************************************** ****************************************************
32位或64位JVM
操作系统 | Java堆大小 | 32位或64位JVM |
视窗 | 小于1300 MB | 32位 |
视窗 | 介于1500 MB和32 GB之间[*] | 64位-d64 -XX:+ UseCompressedOops命令行选项 |
视窗 | 超过32 GB | 带-d64命令行选项的64位 |
的Linux | 小于2 GB | 32位 |
的Linux | 2到32 GB之间[*] | 64位-d64 -XX:+ UseCompressedOops命令行选项 |
的Linux | 超过32 GB | 带-d64命令行选项的64位 |
Oracle Solaris | 小于3 GB | 32位 |
Oracle Solaris | 介于3到32 GB之间[*] | 64位-d64 -XX:+ UseCompressedOops命令行选项 |
Oracle Solaris | 超过32 GB | 带-d64命令行选项的64位 |
[*]在具有-XX:+UseCompressedOops
的64位HotSpot VM中,最佳性能约为最大Java堆大小约26 GB或更少。
Java 6 Update 18以后的HotSpot VM版本默认基于最大Java堆大小自动启用-XX:+UseCompressedOops
。
****************************************************** ****************************************************
从parallelOldDC开始:年轻的和老的收藏是多线程的
吞吐量垃圾收集器由HotSpot VM命令行选项-XX:+UseParallelOldGC
或-XX:+UseParallelGC
。 如果-XX:+UseParallelOldGC
在您使用的HotSpot VM版本中不可用,请使用-XX:+UseParallelGC
。 两者之间的区别在于-XX:+UseParallelOldGC
启用了多线程的年轻代垃圾收集器和多线程的旧世代垃圾收集器,即次要垃圾收集和完整垃圾收集都是多线程的。 -XX:+UseParallelGC
仅启用多线程年轻代垃圾收集器。 与-XX:+UseParallelGC
一起使用的旧式垃圾收集器是单线程的。 使用-XX:+UseParallelOldGC
也会自动启用-XX:+UseParallelGC
。 因此,如果要同时使用多线程年轻代垃圾收集器和多线程旧代垃圾收集器,则只需指定-XX:+UseParallelOldGC
。
****************************************************** ****************************************************
垃圾收集日志记录:
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<filename>
在将HotSpot VM调整为低延迟时,以下两个命令行选项很有用,因为它们报告了由于VM安全点操作而导致应用程序被阻止的时间,以及应用程序在安全点操作之间执行了多长时间。
• -XX:+PrintGCApplicationStoppedTime
• -XX:+PrintGCApplicationConcurrentTime
选项-XX:+PrintSafepointStatistics
可以帮助区分垃圾回收安全点和其他安全点。
可以使用-XX:+PrintGCApplicationConcurrentTime
命令行选项来确定应用程序在感兴趣的某个观察到的响应时间超过应用程序要求的某个时间段内是否正在执行以及执行了多长时间。
****************************************************** ****************************************************
建议的GC日志记录命令行选项
GC命令行选项 | 最适用 |
-XX:+ PrintGCTimeStamps -XX:+ PrintGC详细信息 -Xloggc:<文件名> | 为所有应用程序启用的最少的命令行选项集。 |
-XX:PrintGCDateStamps | 在希望查看日历日期和时间而不是指示自JVM启动以来的秒数的时间戳时使用。 需要Java 6 Update 4或更高版本。 |
-XX:+ PrintGCApplicationStoppedTime -XX:+ PrintGCApplicationConcurrentTime -XX:+ PrintSafepointStatistics | 在调整应用程序以降低响应时间/延迟时很有用,有助于区分VM安全点操作和其他来源引起的暂停事件。 |
XX:+PrintCommandLineFlags
在HotSpot VM初始化时将选定的初始堆大小和最大堆大小打印为-XX:InitialHeapSize=<n>
-XX:+PrintTenuringDistribution
****************************************************** ****************************************************** ************************
-XX:MaxHeapSize=<m>
,其中<n>
是初始Java堆大小(以字节为单位),而<m>
是最大Java堆大小(以字节为单位)
****************************************************** ****************************************************** ************************
空间 | 命令行选项 | 占用率 |
Java堆 | -Xms和-Xmx | 完全垃圾收集后,老一代空间占用率为3到4倍 |
永久世代 | -XX:PermSize -XX:MaxPermSize | 完全垃圾收集后,永久生成空间占用率为1.2x到1.5x |
年轻一代 | -Xmn | 完全垃圾收集后,老一代空间占用率为1x到1.5x |
老一辈 | 从Java总堆大小减去年轻代大小中隐含 | 完全垃圾收集后,老一代空间占用率为2到3倍 |
****************************************************** ****************************************************** ************************
年轻空间
-XX:NewSize=<n>[g|m|k]
-XX:MaxNewSize=<n>[g|m|k]
-Xmn<n>[g|m|k]
-Xmn可以方便地确定年轻一代空间的初始大小和最大大小
如果(-Xmx != -Xms) && -Xmn
存在
Java堆大小的增长或收缩不会调整年轻代空间的大小。
随Java堆大小的任何增长或收缩,年轻代空间的大小将保持不变。 因此,仅当-Xms
和-Xmx
设置为相同的值时, -Xmn
应使用-Xmn
。
****************************************************** ****************************************************** ************************
烫发大小
-XX:PermSize=<n>[g|m|k]
-XX:MaxPermSize=<n>[g|m|k]
强调性能的Java应用程序应将初始和最大永久生成大小( -XX:PermSize
和-XX:MaxPermSize
)的大小都设置为相同的值,因为增加或缩小永久生成空间需要完整的垃圾收集。
****************************************************** ****************************************************** ************************
-XX:-ScavengeBeforeFullGC
将在完整垃圾收集上禁用年轻一代空间垃圾收集。
****************************************************** ****************************************************** ************************
2010-11-25T18:51:03.895-0600: [Full GC
[PSYoungGen: 279700K->267300K(358400K)][ParOldGen: 685165K->685165K(685170K)]
964865K->964865K(1043570K)
[PSPermGen: 32390K->32390K(65536K)],
0.2499342 secs]
[Times: user=0.08 sys=0.00, real=0.05 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space2010-11-25T18:26:37.755-0600: [Full GC
[PSYoungGen: 0K->0K(141632K)]
[ParOldGen: 132538K->132538K(350208K)]
32538K->32538K(491840K)[PSPermGen: 65536K->65536K(65536K)],
0.2430136 secs]
[Times: user=0.37 sys=0.00, real=0.24 secs]
java.lang.OutOfMemoryError: PermGen space
如果您在垃圾回收日志中观察到OutOfMemoryError
,请尝试将Java堆大小增加到JVM可用物理内存的80%到90%。
例如,对于旧世代空间OutOfMemoryErrors,增加-Xmsand -Xmx
,对于永久世代OutOfMemoryErrors,增加-XX:PermSize
和-XX:MaxPermSize
。
下一步是计算应用程序的实时数据大小。
****************************************************** ****************************************************** ************************
计算实时数据大小
除了实时数据大小外,处于稳定状态的完整垃圾收集还提供了由于完整垃圾收集而导致的最坏情况下的延迟。
您可以使用实时数据(应用程序繁忙时间内的perm空间+满GC后的旧空间)来计算初始堆大小:
通常,初始和最大Java堆大小命令行选项-Xms和-Xmx的值应设置为旧空间的实时数据大小的三到四倍。
完全垃圾回收后的旧一代空间占用为295111K,或大约295 MB。因此,实时数据大小约为295 MB。
因此,为此应用程序指定的建议的初始Java堆大小和最大Java堆大小应为885到1180兆字节之间的值,即-Xms1180m -Xmx1180m是实时数据大小的四倍。
同样作为一般规则,初始和最大永久世代大小-XX:PermSize
和-XX:MaxPermSize
应该比永久世代空间的活动数据大小大1.2倍至1.5倍。
在上图所示的示例完整垃圾收集中,完整垃圾收集后的永久生成空间占用为32390K,约32兆字节。 因此,为此应用程序指定的建议的初始和最大永久生成空间大小应在38兆字节和48兆字节之间,即-XX:PermSize = 48m -XX:MaxPermSize = 48m,是永久生成实时数据大小的1.5倍。
作为附加的一般规则,年轻代空间应为旧代空间活动数据大小的1到1.5倍。
结果,建议的年轻代大小应在295到442兆字节之间。 在上图中,年轻一代的空间大小为358400K,约358兆字节。 358兆字节在建议的大小以内。
如果初始和最大Java堆大小是实时数据大小的3倍至4倍,而年轻代空间是实时数据大小的1倍至1.5倍,则旧代空间的大小应在实时数据大小的2倍至3倍之间。
基于上图中的垃圾收集数据,应用这些常规大小调整规则的组合Java命令行为
$ java -Xms1180m -Xmx1180m -Xmn295m
-XX:PermSize=48m -XX:MaxPermSize=48m
空间 | 命令行选项 | 占用率 |
Java堆 | -Xms和-Xmx | 完全垃圾收集后,老一代空间占用率为3到4倍 |
永久世代 | -XX:PermSize -XX:MaxPermSize | 完全垃圾收集后,永久生成空间占用率为1.2x到1.5x |
年轻一代 | -Xmn | 完全垃圾收集后,老一代空间占用率为1x到1.5x |
老一辈 | 从Java总堆大小减去年轻代大小中隐含 | 完全垃圾收集后,老一代空间占用率为2到3倍 |
****************************************************** ****************************************************** ************************
音调延迟/响应能力
评估垃圾收集器对延迟的影响涉及以下活动:
- 测量次要垃圾收集时间
- 测量次要垃圾收集频率
- 测量最坏情况下的完整垃圾收集持续时间
- 测量最坏情况下的全部垃圾收集频率
调整年轻代的大小
从parallelGC到并发-XX:+UseConcMarkSweepGC
如果最坏的情况下,完整垃圾收集持续时间或频率收集频率太高
随着年龄的增长,要记住的其他一般准则是
- 老一代的空间大小不应小于实时数据大小的1.5倍。 有关实时数据大小的定义和其他旧版本调整大小的准则,请参见上一节“ 确定内存占用空间 ”。
- 年轻代的空间大小至少应为Java堆大小的10%,该值指定为-Xmx和-Xms。 很小的年轻一代可能适得其反。 它导致频繁的次要垃圾收集。
- 当增加Java堆大小时,请注意不要超过JVM可用的物理内存量。 Java堆大小消耗的内存足以导致基础系统交换到虚拟内存,从而导致垃圾收集器和应用程序性能下降。
调整旧一代的大小
这项任务的目的是评估由完整垃圾收集引起的最坏情况下的暂停时间以及完整垃圾收集的频率。
如果您仅观察完整的垃圾收集
当修改旧世代空间的大小时,旧世代大小可能会与年轻世代大小失去平衡,并导致应用程序仅经历完整的垃圾回收。 通常,这发生在旧的一代空间不够大而无法容纳所有从年轻一代空间提升的对象时,即使在进行完整的垃圾回收之后。
老式空间不够大的关键指标是,老式空间中的回收空间很小(ParOldGen标签右侧的值),并且在每个完整空间之后,大部分年轻空间仍然被占用。垃圾收集。 当旧一代中没有足够的空间来处理年轻一代的提升对象时,如先前输出中所观察到的,对象将“备份”到年轻一代空间中。
如果由于完整的垃圾收集持续时间太长而无法满足应用程序最坏情况下的延迟要求,那么您应该切换到使用并发垃圾收集器 。
使用HotSpot命令行选项启用并发垃圾收集器:
-XX:+UseConcMarkSweepGC
****************************************************** ****************************************************** ************************
避免幸存者空间溢出是通过调整幸存者空间的大小来实现的,以使幸存者空间足够大,可以将幸存的对象放置足够长的时间,以便在一段时间内老化。 有效的老化只会将寿命长的物体提升到旧的空间。
小费
老化是将对象保留在年轻一代中直至无法再访问的一种方法,以便为寿命更长的对象保留老一代空间。
使用HotSpot命令行选项调整幸存者空间的大小:
-XX:SurvivorRatio=<ratio>
<ratio>
的值必须大于0。- -XX:SurvivorRatio=<ratio>
表示每个幸存者空间与eden空间之间的空间比率。 以下公式可用于确定幸存者空间大小:
生存者空间大小= -Xmn<value>/(-XX:SurvivorRatio=<ratio> + 2)
指定为比率的值越大,幸存者空间大小越小。
权属阈值说明
小费
在年轻一代中进行有效的对象老化,以防止它们过早地提升到老一代空间,这降低了老一代的占用率。 这减少了CMS垃圾回收周期必须执行的频率,还减少了碎片的可能性。
还有一个HotSpot虚拟机命令行选项, -XX:MaxTenuringThreshold=<n>
可用于询问HotSpot虚拟机仅一个对象的年龄之后,以促进对象的老一代空间超出的值<n>
对于Java 5 Update 6和更高版本,最大使用期限阈值可以设置为0–15,对于Java 5 Update 5和更低版本,可以设置为0–31。
不建议将最大使用期限阈值设置为0。这会导致在分配对象后,在下一个次要垃圾回收中将对象立即从年轻一代提升到老一代。 这将非常Swift地扩展旧一代的空间,并导致频繁的完整垃圾收集。
也建议不要将最大使用期限阈值设置为大于可能的最大值。 这将导致对象保留在幸存者空间中,直到幸存者空间溢出为止。 如果它们溢出,则对象将被无差别地提升为老一代,也就是说,它们不会根据其年龄来提升。 结果,可以在寿命较长的对象之前提升寿命较短的对象,这防止了有效的对象老化。
小费
通常,观察新的占位阈值始终小于最大占位阈值,或者观察所需的幸存者大小小于幸存总字节数(对象年龄的最后一行和最右边的列的值)表明幸存者空间太小。
****************************************************** ****************************************************** ************************
启动CMS收集周期
停止世界压缩垃圾收集是最坏情况下垃圾收集导致的延迟
CMS周期的启动基于旧空间的占用
如果观察到世界各地的压缩垃圾收集,则可以调整CMS周期何时开始。 并发模式故障会在垃圾收集输出中标识CMS中的“停止世界”压缩垃圾收集。 以下是一个示例:
174.445: [GC 174.446: [ParNew: 66408K->66408K(66416K), 0.0000618
secs]174.446: [CMS (concurrent mode failure): 161928K->162118K(175104K),
4.0975124 secs] 228336K->162118K(241520K)
如果您在垃圾回收输出中观察到并发模式故障,则可以使用命令行选项指示HotSpot VM提前启动CMS周期的开始:
-XX:CMSInitiatingOccupancyFraction=<percent>
指定的值是CMS垃圾回收周期应开始的旧占用率的百分比。 例如,如果您希望CMS周期从旧的空间占用率为65%开始,请设置-XX:CMSInitiatingOccupancyFraction=65
。 第二个HotSpot命令行选项应与– XX:CMSInitiatingOccupancyFraction=<percent>
结合使用
-XX:+UseCMSInitiatingOccupancyOnly
-Xmx1536m -Xms1536m -Xmn512m
-XX:CMSInitiatingOccupancyFraction = 51
-XX:+仅使用CMSInitiatingOccupancy
显式垃圾回收
如果观察到完整的垃圾回收,这些回收是通过显式调用System.gc()
,那么在使用并发垃圾回收器时有两种处理方法:
- 您可以使用HotSpot VM命令行选项请求HotSpot VM作为并发垃圾回收周期执行它们:
- -XX:+ ExplicitGCInvokesConcurrent
- 要么
- -XX:+ ExplicitGCInvokesConcurrentAndUnloadsClasses
- 首先需要Java 6或更高版本。 第二个要求Java 6 Update 4或更高版本。 如果您正在使用的JDK版本支持-XX:+ ExplicitGCInvokesConcurrentAndUnloadsClasses,通常会更好。
- 您可以使用Hotspot命令行选项让HotSpot VM忽略对System.gc()的显式调用:
- -XX:+ DisableExplicitGC
- 此命令行选项还忽略其他HotSpot VM垃圾收集器中对System.gc()的显式调用。
尽管在垃圾收集输出中报告了CMS Perm标签,但默认情况下,HotSpot VM不会使用CMS垃圾收集永久生成空间。 要启用CMS永久生成垃圾收集,您必须指定以下HotSpot VM命令行选项:
-XX:+CMSClassUnloadingEnabled
如果使用的是Java 6 Update 3或更早版本,则除了-XX:+ CMSClassUnloadingEnabled外,还必须指定以下命令行选项: -XX:+CMSPermGenSweepingEnabled
CMS暂停时间调整
备注阶段中使用的线程数可以由以下HotSpot VM命令行选项控制:
-XX:ParallelGCThreads=<n>
在某些情况下,可以通过指定以下内容来缩短备注阶段的持续时间:
-XX:+CMSScavengeBeforeRemark
此命令行选项强制HotSpot VM在CMS注释之前执行次要垃圾回收。 在备注之前进行次要的垃圾收集可以通过减少年轻一代空间中可以从旧一代空间到达的对象的数量来最大程度地减少备注阶段的工作量。
如果应用程序中有大量要处理的引用或可终结对象,则指定以下HotSpot VM命令行选项可以帮助减少垃圾收集时间:
-XX:+ParallelRefProcEnabled
最新和最优化
当新的性能优化集成到HotSpot VM中时,通常在命令行选项-XX:+AggressiveOpts
下引入它们。
如果应用程序涉众正在寻求其他性能并愿意接受与启用最新优化相关的其他小风险,则应考虑使用-XX:+AggressiveOpts
命令行选项。
转义分析
转义分析是一种评估Java对象范围的技术。 特别是,如果某个执行线程分配的Java对象可以被其他线程看到,则该对象“转义”。 如果Java对象无法逃脱,则可以应用其他优化技术。 因此,优化技术称为逃逸分析。
使用以下命令行选项启用HotSpot VM中的转义分析优化:
-XX:+DoEscapeAnalysis
Linux上的大页面
-XX:+UseLargePages
翻译自: https://www.javacodegeeks.com/2013/12/garbage-collector-guidelines-and-tips.html
垃圾收集算法,垃圾收集器