美文网首页
减少长时间的GC停顿

减少长时间的GC停顿

作者: 专职掏大粪 | 来源:发表于2020-08-26 11:41 被阅读0次

长GC暂停对于应用程序是不希望的。它会影响您的SLA;它导致不良的客户体验,并严重损害关键任务应用程序。因此,在本文中,我提出了可能导致长时间GC暂停的关键原因以及解决这些问题的潜在解决方案

1.高对象创造率

如果您的应用程序的对象创建率很高,因此,垃圾回收率也将非常高。高垃圾收集率也会增加GC暂停时间。因此,优化应用程序以创建较少数量的对象是减少长GC暂停的有效策略。这可能是比较耗时,但值得100%进行。为了优化应用程序中的对象创建速度,您可以考虑使用Java Profiler(如 JProfiler, YourKit,JVisualVM ...)。这些分析器将报告

  • 创建的对象是什么?
  • 这些对象的创建速率是多少?
  • 它们在内存中占用多少空间?
  • 谁在创造它们?
    一直尝试优化占用最大内存量的对象

小提示:如何计算对象创建率?

2.年轻的空间过小

当年轻代太小时,对象将被过早地提升为老代。从老年代收集垃圾要比从年轻代收集垃圾花费更多的时间。因此,增加年轻代的大小可以减少长时间的GC暂停。可以通过设置两个JVM参数中的任何一个来增加年轻代

  • -Xmn:指定年轻代的大小

  • -XX:NewRatio:指定老年代和年轻代的比例。例如,设置-XX:NewRatio=3,表示老一代和年轻一代的比例是3:1。也就是说,年轻代将占整个堆的四分之一。例如,如果堆大小是2 GB,那么年轻一代的大小将是0.5 GB

3. GC算法的选择

GC算法的选择对GC暂停时间有很大影响。除非您是GC专家或者打算成为一个专家或者团队中的某人是GC专家,否则您可以调整GC设置以获得最佳的GC暂停时间。假设您不具备GC专业知识,那么我建议您使用G1 GC算法,因为它具有 自动调整功能。在G1 GC中,您可以使用系统属性“ -XX:MaxGCPauseMillis”设置GC暂停时间目标。例:

 -XX:MaxGCPauseMillis = 200 

根据上面的示例,最大GC暂停时间设置为200毫秒。这是一个软目标,JVM将尽力实现这一目标。如果您已经在使用G1 GC算法,并且仍然继续经历高暂停时间,请参考本文。

4. 内存交换

有时由于内存不足(RAM),操作系统可能正在从内存中交换应用程序.Swapping非常昂贵,因为它需要磁盘访问,这比物理内存访问要慢得多.交换过程时,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+$SWAP
                done
                if (( $SUM > 0 )); then
                    echo "PID=$PID swapped $SUM KB ($PROGNAME)"
                fi
                let OVERALL=$OVERALL+$SUM
                SUM=0
            done
            echo "Overall swap used: $OVERALL KB" 
                        

如果发现进程正在交换,请执行以下操作之一:

a。向服务器分配更多RAM

b。减少服务器上运行的进程数,以便它可以释放内存(RAM)。

C。减小应用程序的堆大小(我不建议这样做,因为它可能导致其他副作用).

5. GC Threads过少

对于GC日志中报告的每个GC事件,将打印user,sys和real time。例:

 [Times: user=25.56 sys=0.35, real=20.48 secs]

(如果在GC事件中您始终注意到“real time”并不比“user”时间显着少,则可能表明GC线程不足。考虑增加GC线程数。假设“user”时间为25秒,并且您已将GC线程数配置为5,那么“real time”应接近5秒(因为25秒/ 5个线程= 5秒)。

警告:添加过多的GC线程将消耗大量CPU,并会占用应用程序的资源。因此,您需要在增加GC线程数之前进行彻底的测试

6.后台IO流量

如果文件系统的I / O活动繁重(即发生大量读取和写入操作),也会导致长时间的GC暂停。这种繁重的文件系统I / O活动可能不是由您的应用程序引起的。可能是由于同一服务器上正在运行的另一个进程引起的,仍然可能导致您的应用程序长时间处于GC暂停状态(https://engineering.linkedin.com/blog/2016/02/eliminating-large-jvm-gc-pauses-caused-by-background-io-traffic)

当I / O繁忙时,您会发现“real time”时间要比“user”时间长得多。例:

 [时间:用户= 25.56 sys = 0.35,实际= 20.48秒]

当发生这种模式时,可以使用以下解决方案:

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

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

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

Tit-bit: 如何监控io?
您可以在Unix中使用sar(系统活动报告)监视I/O活动。例子:

 sar -d -p 1

上面的命令报告每1秒对设备所做的读/秒和写/秒。有关“sar”命令的更多细节,请参阅本教程。

7. 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参数'-XX:+ DisableExplicitGC'来强制禁用System.gc()调用
Tit-bit:如何知道是否显式调用了System.gc()调用?

image.png

8. 分配过大的堆空间

大堆大小(-Xmx)也可能导致长时间的GC暂停。如果堆大小很大,那么堆中将堆积更多的垃圾。当触发Full GC来清除堆中所有累积的垃圾时,将需要很长时间才能完成。逻辑很简单:如果您的小罐子里装满了垃圾,将可以轻松快捷地进行处理。另一方面,如果您有卡车装满的垃圾,将需要更多的时间来处理它们。

假设您的JVM堆大小为18GB,然后考虑拥有三个6 GB JVM实例,而不是一个18GB JVM。较小的堆大小具有降低长时间GC暂停的巨大潜力。

注意:上述所有策略均应在经过全面测试和分析后才能投入生产。所有策略可能不适用于您的应用程序。这些策略使用不当会导致负面结果

9.Workload distribution

即使有多个GC线程,有时工作负载也会在GC工作线程之间平均分配。有很多原因导致GC工作负载可能无法平均分配到GC线程中。例如:

a。当前无法并行扫描大型线性数据结构。

b。某些事件仅触发单个线程收集器(例如,CMS收集中出现“并发模式故障”时)

如果碰巧使用CMS(并发标记和清除算法),则可以考虑传递 -XX:+ CMSScavengeBeforeRemark参数。这样可以在GC工作线程之间创建更加平衡的工作负载

CMSScavengeBeforeRemark:CMS并发标记阶段与用户线程并发进行,此阶段会产生已经被标记了的对象又发生变化的情况,若打开此开关,可在一定程度上降低CMS重新标记阶段对上述“又发生变化”对象的扫描时间,当然,“清除尝试”也会消耗一些时间
注,开启此开关并不会保证在标记阶段前一定会进行清除操作

翻译自
https://gceasy.io/gc-recommendations/long-pause-solution.jsp

相关文章

  • 减少长时间的GC停顿

    长GC暂停对于应用程序是不希望的。它会影响您的SLA;它导致不良的客户体验,并严重损害关键任务应用程序。因此,在本...

  • 如何减少长时间的 GC 停顿?

    简书 涤生[https://www.jianshu.com/users/150f36a73910/]。转载请注明原...

  • 如何减少长时间的 GC 停顿

    垃圾回收是非常必要的,但是如果处理不好,它会成为性能杀手。采取以下步骤以确保 GC 停顿时间最少且最短。 长时间的...

  • 应用GC长时间停顿分析

    早上被报警叫醒,使用gceasy.io分析了服务器的gc日志,报告见:2017-05-28 gc.log报告 这份...

  • 如何减少GC停顿时间

    高效率创建对象,可以通过GC日志分析工具GCeasy进行分析 合理设置年轻代空间大小,避免对象过早提升到老年代。一...

  • 反射解决安卓里的TimeoutException

    TimeoutException 错误堆栈信息: 首先来说明一下发生问题的原因,在GC时,为了减少应用程序的停顿,...

  • JVM-GC日志分析

    全量GC日志 分段分析(一) GC发生时间 GC 或 Full GC 表示垃圾收集器停顿类型,新生代GC还是老年代...

  • CMS相关flag

    CMS被设计为尽可能的减少jvm停顿时间的垃圾收集器,使用标记-清除算法思想,其gc周期分为以下几个过程: Ini...

  • 美团面试官问我:ZGC 的 Z 是什么意思?

    本文的阅读有一定的门槛,请先了解 GC 的基本知识。 现代垃圾收集器的演进大部分都是往减少停顿方向发展。 像 CM...

  • 什么是GC停顿,为什么要停顿?

    GC停顿(STW):Stop The World,即暂停所有用户线程。因为只有暂停所有用户线程,才能保证系统不会产...

网友评论

      本文标题:减少长时间的GC停顿

      本文链接:https://www.haomeiwen.com/subject/nhxzjktx.html