JVM系列(三十八) JVM调优实战-Eden区年轻代毛刺及陡峰问题排查

422 阅读4分钟

1.Eden区为什么会出现内存空间的陡升陡降

根据JVM内存的监控信息,我们发现下午17:00的时候,内存新生代Eden区明显出现了增大的情况

  • 内存空间出现陡升,从原来的平均200M , 升为平均 1G左右
  • 肯定是有什么操作触发了大量的操作对象信息,频繁的创建对象,Eden区增大
  • 然后触发YongGC对对象回收,销毁这部分新建的对象,Eden区减小
  • 所以才会出现 频繁的陡增,陡降的问题

为什么会出现内存空间年轻代的陡升和陡降?看下问题排查统计

  • RPC请求接口信息如下
  • RPC请求与过去1小时对比,与昨天此刻时间对比, 并未发现明显的上升
  • 所以不是因为RPC 过量调用导致的问题
  • 同理,检测HTTP接口 此刻是否有明显的剧增,也没有,排除HTTP请求量剧增的场景

image.png

2.业务分析的大对象信息

我们的统计信息,打印了业务涉及的大对象信息,发现出现了一个执行计划中,节点个数特别多的场景,如下

image.png

也就是说 发现 3700个节点的执行计划 查询了 2.4W次, 把这些节点对象加载进内存进行处理,有可能会导致对象增多, 到底是不是这个问题导致的呢?

下面我们来分析下,这些对象到底占用多少内存,能否与我们的Eden区的内存曲线 匹配上

2.1 节点到底占用多少内存

上面我们知道,有可能是3700个节点的执行计划,执行查询了2.4W次,到底这些对象经过多次查询,在内存中占用多少内存空间呢? 3700个节点,到底 占了多少M内存呢?

  • 一个字段varchar32位, 4Byte 字节
  • 一个记录 节点大概有 30个字段, 那就是 30*4 = 120Byte 字节
  • 经过RPC序列化, DB写库,缓存,IO通信, 复制拷贝 一般一个对象被放大 20-50倍 就按 放大30倍, 一条节点信息 记录就是 30*120B=3600B = 3.6KB
  • 一条记录 3.6KB , 现在有 3700个对象,, 那就是 3.6 * 3700 =  3.6* 3.7M = 13.32M
  • 也 就是 该执行计划所有对象查询一次,占用内存大约 13.32M
  • 该对象伴随着 每一次的线程访问,都会伴随不同的线程去创建,也就是一个接口访问一次 就会创建一次
  • 我们来看下访问频率信息 5分钟/ 300秒内 访问了2.7W次,就按照 3W次方便计算,30000次/300s = 100次/秒
  • 每秒 就是访问 100次, 创建100个对象, 一个对象 13.32M, 每秒创建 100*13.32M = 1332M = 1.33G, 占用内存 1.3G
  • 对象创建在Eden区初始化, 访问结束后对象销毁, 频繁的创建/销毁 
  • 导致JVM 出现陡升陡降,产生如图的Eden去内存信息波浪线 

大对象频繁的创建,销毁就会导致Eden区内存信息的陡升陡降波浪线, 通过内存大小的推算,也符合JVM检测的内存曲线,确定就是执行计划的节点数产生的问题

3.如何解决问题

那么如何避免这种问题呢?

  1. 首先肯定是需求层面限制,限制执行计划的节点数,比如限制每条执行计划,流程节点不允许超过1000个,从产品需求上进行限制,不允许创建流程
  2. 技术上实现 扩大Eden区大小,防止他频繁的YoungGC,但是这种该方式不推荐,因为G1是动态调节Eden区的,如果设置了参数,现在Eden区大小,这种不利于G1动态调优
  3. 业务上 对大对象,尽量使用一次,不要频繁的复制,copy或者转换Map操作,这样都会对复制该对象,导致空间减少