如何减少长时间的GC暂停?

流过昼夜 提交于 2020-03-08 21:12:41

长时间的GC暂停对于应用程序而言是不可取的。它会影响您的SLA,导致不良的客户体验,并对关键任务应用程序造成严重损害。因此,在本文中,我列出了可能导致长时间GC暂停的主要原因,以及解决这些问题的潜在解决方案。
1.高对象创造率
如果您的应用程序的对象创建率很高,那么为了跟上它,垃圾回收率也将很高。高垃圾回收率也会增加GC暂停时间。因此,优化应用程序以创建更少的对象是减少长时间GC暂停的有效策略。这可能是一个耗时的练习,但值得100%进行。为了优化应用程序中的对象创建速率,可以考虑使用Java Profiler(如 JProfiler, YourKit或JVisualVM)。这些分析器将报告:

创建了哪些对象?


这些对象的创建速率是多少?


它们在内存中占用多少空间?


谁在创造它们?

始终尝试优化占用最大内存量的对象。追捕池塘里的大鱼。
.年龄不足的年轻一代
当年轻一代过小时,对象会过早地提升到老一代。从年轻一代收集垃圾比从年轻一代收集垃圾要花费更多的时间。因此,增加年轻代的大小有可能减少长时间的GC暂停。可以通过设置两个JVM参数之一来增加年轻一代的大小
-Xmn:指定年轻代的大小。
-XX:NewRatio:指定年轻一代相对于老一代的大小。例如,设置-XX:NewRatio = 2表示老一代与年轻一代之间的比率为1:2。年轻一代将是整个堆的一半。因此,如果堆大小为2 GB,则年轻代大小将为1 GB。
3. GC算法的选择
GC算法对GC暂停时间有很大影响。如果您是GC专家或打算成为一名GC专家(或者您团队中的某人是GC专家),则可以调整GC设置以获得最佳的GC暂停时间。如果您没有很多GC专业知识,那么我建议您使用G1 GC算法,因为它具有 自动调整 功能。在G1 GC中,您可以使用系统属性“ -XX:MaxGCPauseMillis”设置GC暂停时间目标。例:

  • XX:MaxGCPauseMillis = 200

根据上面的示例,最大GC暂停时间设置为200毫秒。这是一个软目标,JVM将尽力实现这一目标。
4.交换流程
有时由于缺少内存(RAM),操作系统可能会从内存中交换应用程序。交换非常昂贵,因为它需要磁盘访问,与物理内存访问相比要慢得多。以我的拙见,在生产环境中没有认真的应用可以互换。交换过程时,GC将花费很长时间才能完成。
下面是从StackOverflow获得的脚本 (感谢作者),该脚本 在执行时将显示所有正在交换的进程。请确保您的进程没有被交换。

#!/bin/bash

Get current swap usage for all running processes

Erik Ljungstrom 27/05/2011

Modified by Mikko Rantalainen 2012-08-09

Pipe the output to “sort -nk3” to get sorted output

Modified by Marc Methot 2014-09-18

removed the need for sudo

SUM=0
OVERALL=0
for DIR in find /proc/ -maxdepth 1 -type d -regex "^/proc/[0-9]+"
do
PID=echo $DIR | cut -d / -f 3
PROGNAME=ps -p $PID -o comm --no-headers
for SWAP in grep VmSwap $DIR/status 2>/dev/null | awk '{ print $2 }'
do
let SUM=SUM+SUM+SWAP
done
if (( SUM>0));thenecho"PID=SUM > 0 )); then echo "PID=PID swapped SUMKB(SUM KB (PROGNAME)"
fi
let OVERALL=OVERALL+OVERALL+SUM
SUM=0
done
echo “Overall swap used: $OVERALL KB”
如果发现进程正在交换,请执行以下一项操作:
为服务分配更多的RAM。
减少服务器上运行的进程数,以便它可以释放内存(RAM)。
减少应用程序的堆大小(我不建议这样做,因为它可能导致其他副作用。但是,它有可能解决您的问题)。
5.调整GC线程数
对于GC日志中报告的每个GC事件,将打印用户,sys和实时。例:
[ 时间:用户= 25.56 sys = 0.35,实际= 20.48 秒 ]

要了解这些时间之间的区别,请 阅读本文。(我强烈建议您在继续本节之前先阅读本文)。如果在GC事件中,您始终注意到“实时”时间并不比“用户”时间显着少,则可能表明GC线程不足。考虑增加GC线程数。假设“用户”时间为25秒,并且您已将GC线程数配置为5,那么实际时间应接近5秒(因为25秒/ 5个线程= 5秒)。
警告:添加过多的GC线程将消耗大量CPU,并占用应用程序资源。因此,您需要在增加GC线程数之前进行彻底的测试。
6.后台IO流量
如果文件系统的I / O活动繁重(即发生大量读写操作),这也可能导致长时间的GC暂停。这种繁重的文件系统I / O活动可能不是由您的应用程序引起的。可能是由同一台服务器上运行的另一个进程引起的。仍然可能导致您的应用程序遭受长时间的GC暂停。这是LinkedIn工程师的精彩 文章 ,详细介绍了此问题。
当繁琐的I / O活动时,您会注意到“实时”时间明显高于“用户”时间。例:
[ 时间:用户= 0.20 sys = 0.01,实际= 18.45 秒 ]

发生这种情况时,以下是一些可能的解决方案:

如果您的应用程序引起了很高的I / O活动,请对其进行优化。


消除导致服务器上大量I / O活动的进程。


将您的应用程序移到I / O活动较少的其他服务器上。

提示:如何监视I / O活动
您可以在Unix中使用sar (系统活动报告) 监视I / O活动 。例:
sar -d -p 1

上面的命令每1秒报告一次对设备的读取/秒和写入/秒。有关“ sar”命令的更多详细信息,请参考 本教程。
7. System.gc()调用
当 System.gc()的 或 调用Runtime.getRuntime()。GC() 方法调用被调用时,它会导致停止的世界充满选区。在世界各地的完整GC期间,整个JVM被冻结(即,在此期间将不执行任何用户活动)。从以下来源之一进行System.gc()调用:
1.您自己的开发人员可能会显式调用System.gc()方法。
2.它可能是第三方库,框架,有时甚至是您使用的应用程序服务器。任何这些都可能调用System.gc()方法。
3.可以通过使用JMX从外部工具(如VisualVM)触发。
4.如果您的应用程序正在使用RMI,则RMI会定期调用System.gc()。可以使用以下系统属性来配置此间隔:
-Dsun.rmi.dgc.server.gcInterval=n
-Dsun.rmi.dgc.client.gcInterval=n
评估是否绝对需要显式调用System.gc()。如果不需要,请删除它。另一方面,可以通过传递JVM参数来强制禁用System.gc()调用:-XX:+ DisableExplicitGC。
最后,开发这么多年我也总结了一套学习Java的资料与面试题,如果你在技术上面想提升自己的话,可以关注我,私信发送领取资料或者在评论区留下自己的联系方式,有时间记得帮我点下转发让跟多的人看到哦。在这里插入图片描述

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!