高吞吐、低延迟Java应用的GC优化实践

原文:https://mp.weixin.qq.com/s/_lXmc7U3bInytb4H1gLeSQ

降低 GC 频率

在分代 GC 算法中,降低 GC 频率可以通过:(1) 降低对象分配(新生代)/晋升率(老年代);(2) 增加各代空间的大小,(3)增加Old GC触发阈值

但是空间并不是越大越好,相反,过大的空间可能会导致GC的停顿时间更长,在 Hotspot JVM 中,Young GC 停顿时间取决于一次垃圾回收后存活下来的对象的数量,而不是 Young Gen 自身的大小。增加 Young Gen 大小对于应用性能的影响需要仔细评估:

  • 如果更多的数据存活而且被复制到 Survivor 区域,或者每次 GC 更多的数据晋升到 Old Gen,增加 Young Gen 大小可能导致更长的 Young GC 停顿。较长的 GC 停顿可能会导致应用程序延迟增加和(或)吞吐量降低。
  • 另一方面,如果每次垃圾回收后存活对象数量不会大幅增加,停顿时间可能不会延长。在这种情况下,降低 GC 频率可能会使整个应用总体延迟降低和(或)吞吐量增加。

对于长期存活对象的应用,就需要注意,被晋升的对象可能很长时间都不能被 Old GC 周期回收。如果 Old GC 触发阈值(Old Gen 占用率百分比)比较低,应用将陷入持续的 GC 循环中。可以通过设置高的 GC 触发阈值可避免这一问题。

由于我们的应用在堆中维持了长期存活对象的较大缓存,将 Old GC 触发阈值设置为

-XX:CMSInitiatingOccupancyFraction=92 -XX:+UseCMSInitiatingOccupancyOnly

来增加触发 Old GC 的阈值。我们也试图增加 Young Gen 大小来减少 Young GC 频率,但是并没有采用,因为这增加了应用的 999 线。

缩短 GC 停顿时间

(1)减小Young Gen(2)减小tenuring threshold(3)减小Old GC触发阈值

减少 Young Gen 大小可以缩短 Young GC 停顿时间,因为这可能导致被复制到 Survivor 区域或者被晋升的数据更少。但是,正如前面提到的,我们要观察减少 Young Gen 大小和由此导致的 GC 频率增加对于整体应用吞吐量和延迟的影响。Young GC 停顿时间也依赖于 tenuring threshold (晋升阈值)和 Old Gen 大小。

我们观察到Eden区域的大部分YoungGen被回收,几乎没有3-8年龄对象在Survivor空间中死亡,也就是说,对于长期存活的对象,不管经历了多少次GC,仍然存活。所以我们将tenuring threshold从8降低到2(使用选项:-XX:MaxTenuringThreshold=2),以降低YoungGC消耗在数据复制上的时间。

在使用 CMS GC 时,应将因堆碎片或者由堆碎片导致的 Full GC 的停顿时间降低到最小。通过控制对象晋升比例和减小 -XX:CMSInitiatingOccupancyFraction 的值使 Old GC 在低阈值时触发。但是,减小该值会增加GC的频率,我们同样需要观察调整该值对系统整体吞吐率的影响。

我们还注意到 Young GC 暂停时间随着 Old Gen 占用率上升而延长。这意味着来自 Old Gen 的压力使得对象晋升花费更多的时间。为解决这个问题,将总的堆内存大小增加到 40GB,减小 -XX:CMSInitiatingOccupancyFraction 的值到 80,更快地开始 Old GC。尽管 -XX:CMSInitiatingOccupancyFraction 的值减小了,增大堆内存可以避免频繁的 Old GC。在此阶段,我们的结果是 Young GC 暂停 70ms,应用的 999 线在 80ms。

优化GC工作线程的任务分配

为了进一步降低 Young GC 停顿时间,我们决定研究 GC 线程绑定任务的参数来进行优化。

-XX:ParGCCardsPerStrideChunk 参数控制 GC 工作线程的任务粒度,可以帮助不使用补丁而获得最佳性能,这个补丁用来优化 Young GC 中的 Card table(卡表)扫描时间。有趣的是,Young GC 时间随着 Old Gen 的增加而延长。将这个选项值设为 32678,Young GC 停顿时间降低到平均 50ms。此时应用的 999 线在 60ms。

还有一些的参数可以将任务映射到 GC 线程,如果操作系统允许的话,-XX:+BindGCTaskThreadsToCPUs 参数可以绑定 GC 线程到个别的 CPU 核。使用亲缘性 -XX:+UseGCTaskAffinity 参数可以将任务分配给 GC 工作线程。然而,我们的应用并没有从这些选项带来任何好处。实际上,一些调查显示这些选项在 Linux 系统不起作用。

了解GC的CPU和内存开销

并发 GC 通常会增加 CPU 使用率。虽然我们观察到 CMS 的默认设置运行良好,但是 G1 收集器的并发 GC 工作会导致 CPU 使用率的增加,显著降低了应用程序的吞吐量和延迟。与 CMS 相比,G1 还增加了内存开销。对于不受 CPU 限制的低吞吐量应用程序,GC 导致的高 CPU 使用率可能不是一个紧迫的问题。

为GC优化系统内存和I/O管理

通常来说,GC 停顿有两种特殊情况:(1) 低 user time,高 sys time 和高 real time
(2) 低 user time,低 sys time 和高 real time。这意味着基础的进程/OS设置存在问题。情况 (1) 可能意味着 JVM 页面被 Linux 窃取;情况 (2) 可能意味着 GC 线程被 Linux 用于磁盘刷新,并卡在内核中等待 I/O。

另外,为了避免在运行时造成性能损失,我们可以使用 JVM 选项 -XX:+AlwaysPreTouch 在应用程序启动时先访问所有分配给它的内存,让操作系统把内存真正的分配给 JVM。我们还可以将 vm.swappability 设置为0,这样操作系统就不会交换页面到 swap(除非绝对必要)。

可能你会使用 mlock 将 JVM 页固定到内存中,这样操作系统就不会将它们交换出去。但是,如果系统用尽了所有的内存和交换空间,操作系统将终止一个进程来回收内存。通常情况下,Linux 内核会选择具有高驻留内存占用但运行时间不长的进程进行终止。在我们的例子中,这个进程很有可能就是我们的应用程序。优雅的降级是服务优秀的属性之一,不过服务突然终止的可能性对于可操作性来说并不好 —— 因此,我们不使用 mlock,只是通过 vm.swapability 来尽可能避免交换内存页到 swap 的惩罚。

最终JVM参数配置:

// JVM sizing options
-server -Xms40g -Xmx40g -XX:MaxDirectMemorySize=4096m -XX:PermSize=256m -XX:MaxPermSize=256m
   
// Young generation options
-XX:NewSize=6g -XX:MaxNewSize=6g -XX:+UseParNewGC -XX:MaxTenuringThreshold=2 -XX:SurvivorRatio=8 -XX:+UnlockDiagnosticVMOptions -XX:ParGCCardsPerStrideChunk=32768

// Old generation  options
-XX:+UseConcMarkSweepGC -XX:CMSParallelRemarkEnabled -XX:+ParallelRefProcEnabled -XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly
   
// Other options
-XX:+AlwaysPreTouch -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:-OmitStackTraceInFastThrow
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259

推荐阅读更多精彩内容