在springboot-admin当中,大概会有以下几种类型的gc出现,本文我们看看他们分别是什么意思。
本文使用的垃圾收集器是jdk1.8的PS+PO。
顾名思义,就是内存分配失败导致的GC,常见于年轻代当中。
使用JNI临界区的方式操作数组或者字符串时,为了防止GC过程中jarray或者jstring发生位移,而导致数组指针失效,需要保持它们在JVM Heap中的地址在JNI Critical过程中保持不变。于是JVM实现了GC_locker,用于JNI Critical内阻止其他GC的发生。
当GCLocker被激活且需要发生GC的时候(这里是否需要GC是各种GC发生时,调用GCLocker::check_active_before_gc()函数check并设置_needs_gc = true的),就会阻塞其他线程进入JNI临界区;并且在最后一个位于JNI临界区的线程退出临界区时,发起一次CGCause为_gc_locker的GC。这里解释了GCLocker Initiated GC发生的原委。
在JVM中的垃圾收集器中的Ergonomics就是负责自动的调解gc暂停时间和吞吐量之间的平衡,使你的虚拟机性能更好的一种做法。
简单说就是内存在进行分配的时候,会通过一些算法,预估是否会出现无法分配的问题。如果符合无法分配预估值,会提前进行一次gc。
这个gc主要发生的条件是元空间,也就是Metadata的参数设置问题。
通常根据我们学习的JVM只是,元空间使用的是本地内存,所以应该与当前服务器的最大内存有关。
但实际不是这样的,在jdk1.8中,如果不设置元空间的大小,会有一个默认值是 21M 。
所以需要我们启动的时候指定一个元空间大小:
GC日志如下所示:
如果您的应用程序的对象创建率很高,因此,垃圾回收率也将非常高。高垃圾收集率也会增加GC暂停时间。因此,优化应用程序以创建较少数量的对象是减少长GC暂停的有效策略。这可能是比较耗时,但值得100%进行。为了优化应用程序中的对象创建速度,您可以考虑使用Java Profiler(如 JProfiler, YourKit,JVisualVM ...)。这些分析器将报告
小提示:如何计算对象创建率?
当年轻代太小时,对象将被过早地提升为老代。从老年代收集垃圾要比从年轻代收集垃圾花费更多的时间。因此,增加年轻代的大小可以减少长时间的GC暂停。可以通过设置两个JVM参数中的任何一个来增加年轻代
GC算法的选择对GC暂停时间有很大影响。除非您是GC专家或者打算成为一个专家或者团队中的某人是GC专家,否则您可以调整GC设置以获得最佳的GC暂停时间。假设您不具备GC专业知识,那么我建议您使用G1 GC算法,因为它具有 自动调整功能。在G1 GC中,您可以使用系统属性“ -XX:MaxGCPauseMillis”设置GC暂停时间目标。例:
根据上面的示例,最大GC暂停时间设置为200毫秒。这是一个软目标,JVM将尽力实现这一目标。如果您已经在使用G1 GC算法,并且仍然继续经历高暂停时间,请参考本文。
有时由于内存不足(RAM),操作系统可能正在从内存中交换应用程序.Swapping非常昂贵,因为它需要磁盘访问,这比物理内存访问要慢得多.交换过程时,GC将花费很长时间才能完成。
下面是从StackOverflow获得的脚本(感谢作者)-执行该脚本 将显示所有正在交换的进程。请确保您的进程没有被交换
如果发现进程正在交换,请执行以下操作之一:
a。向服务器分配更多RAM
b。减少服务器上运行的进程数,以便它可以释放内存(RAM)。
C。减小应用程序的堆大小(我不建议这样做,因为它可能导致其他副作用).
对于GC日志中报告的每个GC事件,将打印user,sys和real time。例:
(如果在GC事件中您始终注意到“real time”并不比“user”时间显着少,则可能表明GC线程不足。考虑增加GC线程数。假设“user”时间为25秒,并且您已将GC线程数配置为5,那么“real time”应接近5秒(因为25秒/ 5个线程= 5秒)。
警告:添加过多的GC线程将消耗大量CPU,并会占用应用程序的资源。因此,您需要在增加GC线程数之前进行彻底的测试
如果文件系统的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”时间长得多。例:
当发生这种模式时,可以使用以下解决方案:
a。如果您的应用程序引起了很高的I / O活动,请对其进行优化。
b。消除导致服务器上大量I / O活动的进程
C。将您的应用程序移到I / O活动较少的其他服务器上
Tit-bit: 如何监控io?
您可以在Unix中使用sar(系统活动报告)监视I/O活动。例子:
上面的命令报告每1秒对设备所做的读/秒和写/秒。有关“sar”命令的更多细节,请参阅本教程。
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()调用?
大堆大小(-Xmx)也可能导致长时间的GC暂停。如果堆大小很大,那么堆中将堆积更多的垃圾。当触发Full GC来清除堆中所有累积的垃圾时,将需要很长时间才能完成。逻辑很简单:如果您的小罐子里装满了垃圾,将可以轻松快捷地进行处理。另一方面,如果您有卡车装满的垃圾,将需要更多的时间来处理它们。
假设您的JVM堆大小为18GB,然后考虑拥有三个6 GB JVM实例,而不是一个18GB JVM。较小的堆大小具有降低长时间GC暂停的巨大潜力。
注意:上述所有策略均应在经过全面测试和分析后才能投入生产。所有策略可能不适用于您的应用程序。这些策略使用不当会导致负面结果
即使有多个GC线程,有时工作负载也会在GC工作线程之间平均分配。有很多原因导致GC工作负载可能无法平均分配到GC线程中。例如:
a。当前无法并行扫描大型线性数据结构。
b。某些事件仅触发单个线程收集器(例如,CMS收集中出现“并发模式故障”时)
如果碰巧使用CMS(并发标记和清除算法),则可以考虑传递 -XX:+ CMSScavengeBeforeRemark参数。这样可以在GC工作线程之间创建更加平衡的工作负载
翻译自
https://gceasy.io/gc-recommendations/long-pause-solution.jsp
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)