性能文章>JVM性能调优--YGC>

JVM性能调优--YGC原创

243915

YGC

YGC频次暂且忽略,问题主要集中在gc耗时上面。想要解决YGC耗时问题,首先要搞清楚YGC的耗时节点。(据我所知的YGC问题,还没有逃出过这些维度)

  • 存活对象扫描、标记时间
  • 存活对象copy to S区,晋升到Old区
  • 等待各线程到达安全点时间
  • GC日志输出
  • 操作系统活动(swap)

1、等待线程到达安全点

GC发生时,程序是会STW的(Serial, ParNew, Parallel Scanvange, ParallelOld, Serial Old全程都会STW,CMS等在初始标记重新标记阶段也会STW), JVM这时候只运行GC线程,不运行用户线程。

那JVM具体要在哪里,在什么时间点STW呢? 答:安全点

因此,GC时,程序需要运行到最近的一个安全点(方法返回、循环结束、异常抛出等位置)停下来,安全点日志前面文章也提到了:如果发现 spin时间段表现异常,那么就要去查看下我们的代码中是否有影响线程快速到达安全点的逻辑块,比如大循环体等。

顺便提一下,安全点的作用不只是进行GC,下面这些点都是会导致程序进入安全点的,其相关问题我们可以单开一篇来叙述

2、存活对象扫描和标记

在程序进入安全点之后,下一步操作就是root对象的标记和引用对象的可达性分析。

2.1 线程栈中的对象引用

这里需要引出两个概念,OopMapRememberedSet

为了获取root对象,jvm需要从线程栈中找到堆内存对象的引用,但是不一定都是引用,如果全栈扫描那就太浪费时间了,怎么办,空间换时间(这个思路简直是到处在用,在之前一篇面试相关的文章中我也有提到),

用OopMap维护对象引用,在到达安全点时,先更新oopMap,然后进行遍历。然后由RememberedSet辅助进行可达性分析。

这里需要注意的是,虽然有了额外存储空间的加持,但是整体的存活对象标记耗时也是不好直接忽视的。

例:某网友的系统,因为采用的调用方式有问题,导致系统中存活的业务线程高达几千之多,那么,要遍历这么多的OopMap的时间消耗也是非常大的。

还有一些程序,因为类似select等查询语句没有添加限制条件,导致大量数据加载进内存,导致GC时间过长,甚至是OOM的发生。

因此,要关注对象引用的个数,包括一个线程中的引用个数和总的线程个数。

2.2 本地缓存存放的对象过多

我们知道,如果对象存活的年限达到了设置的上限,是会晋升老年代的。如果有大量的本地缓存对象被创建,在其晋升到老年代之后,YGC会通过扫描card table来确认其是否存活,从而增加YGC的是存活对象标记时间。

因此,本地缓存方案要评估完整,或者考虑用堆外缓存减少本地缓存对GC的影响。

2.3 system class loader 加载对象过多

如果程序中有解析xml的逻辑时,每new一个XStream对象,就会新创建一个classloader,类加载器会和类的权限定名作为key,value为真正的Klass对象,会存储在SystemDirectionary里,最终越来越多的存活对象存储在内存里,导致需要占用很长时间去标记。

“The XStream instance is thread-safe. That is, once the XStream instance has been created and configured, it may be shared across multiple threads allowing objects to be serialized/deserialized concurrently”.

XStream官方的说法是XStream线程安全,不需要重复初始化xstream对象,为每个反序列化的对象声明一个静态的XStream,重复利用即可。

2.4JNI & Monitor & finalizable等

暂时没有遇到过具体的异常实例,后续遇到了再补充吧。

3、存活对象Copy

可能不是特别准确,因为机器1的GC不一定会发生,只是想用这个简单的例子说明下eden区和新对象大小对GC copy的影响,进而对GC时间产生影响。

所以,要善于运用JVM监控,预估每次调用的新对象和存活对象的大小,结合并发数,设计合理的eden区和survivor区。

4、GC日志输出

GC日志是比较让人忽略的点,但是确实会对GC时间产生负面的影响。因为GC时的STW的总时间内,包含了GC日志打印的时间,正常情况下,输出有限信息的GC日志对GC整体时间的影响应该是微乎其微的,但是如果正好遇到了GC日志输出时系统的IO负载很好,那么可能会在日志输出这里等待很长的时间了。

一个解决办法是将GC日志文件放到tmpfs上(例如,-Xloggc:/tmpfs/gc.log)。因为tmpfs没有磁盘文件备份,所以tmpfs文件不会导致磁盘行为,因此也不会被磁盘IO阻塞。

reference:后台IO异常导致的GC异常 https://www.pianshen.com/article/5926239581/

 

总结:YGC耗时问题可以说千奇百怪,但是万变不离其中,我们只要能够掌握YGC的几个关键节点涉及的影响,从原理着手分析,应该是没有什么大问题的~

分类:标签:
请先登录,查看1条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

不起眼,但是足以让你有收获的JVM内存分析案例
分析 这个问题说白了,就是说有些int[]对象不知道是哪里来的,于是我拿他的例子跑了跑,好像还真有这么回事。点该 dump 文件详情,查看相关的 int[] 数组,点该对象的“被引用对象”,发现所
从一起GC血案谈到反射原理
前言 首先回答一下提问者的问题。这主要是由于存在大量反射而产生的临时类加载器和 ASM 临时生成的类,这些类会被保留在 Metaspace,一旦 Metaspace 即将满的时候,就会触发 Fu
关于内存溢出,咱再聊点有意思的?
概述 上篇文章讲了JVM在GC上的一个设计缺陷,揪出一个导致GC慢慢变长的JVM设计缺陷,可能有不少人还是没怎么看明白的,今天准备讲的大家应该都很容易看明白 本文其实很犹豫写不写,因为感觉没有
协助美团kafka团队定位到的一个JVM Crash问题
概述 有挺长一段时间没写技术文章了,正好这两天美团kafka团队有位小伙伴加了我微信,然后咨询了一个JVM crash的问题,大家对crash的问题都比较无奈,因为没有现场,信息量不多,碰到这类问题我
又发现一个导致JVM物理内存消耗大的Bug(已提交Patch)
概述 最近我们公司在帮一个客户查一个JVM的问题(JDK1.8.0_191-b12),发现一个系统老是被OS Kill掉,是内存泄露导致的。在查的过程中,阴差阳错地发现了JVM另外的一个Bug。这个B
JVM实战:优化我的IDEA GC
IDEA是个好东西,可以说是地球上最好的Java开发工具,但是偶尔也会卡顿,仔细想想IDEA也是Java开发的,会不会和GC有关,于是就有了接下来对IDEA的GC进行调优 IDEA默认JVM参数: -
不起眼,但是足以让你收获的JVM内存案例
今天的这个案例我觉得应该会让你涨姿势吧,不管你对JVM有多熟悉,看到这篇文章,应该还是会有点小惊讶的,不过我觉得这个案例我分享出来,是想表达不管多么奇怪的现象请一定要追究下去,会让你慢慢变得强大起来,
如何通过反射获得方法的真实参数名(以及扩展研究)
前段时间,在做一个小的工程时,遇到了需要通过反射获得方法真实参数名的场景,在这里我遇到了一些小小的问题,后来在部门老大的指导下,我解决了这个问题。通过解决这个问题,附带着我了解到了很多新的知识,我觉得