Java内存问题分析与定位

简介

 

  •  JNI方法申请的native 内存,通常是在JDK库里;本地 C++ 方法直接通过 malloc申请的内存,不受JVM管控。
  • 堆内内存: 指Java堆,GC算法管理的内存区域。
  • 堆外内存: Java堆外的内存都叫堆外。可以细分为JVM内部,Metaspace, JNI方法申请的native内存三部分。通常说的直接内存 DirectBuffer , 会记录在Internal区。

JNI申请的native内存不受JVM管控。

GC算法和日志解读

  • Java堆进一步划分,Eden, s0, s1, 老年代。
  • 不同GC算法对存活对象的扫描和清理方式不一样,对业务代码的运行影响不一样,因此,不同的业务场景,选合适的GC算法和GC参数。

一般建议 parallel scavenge (JDK8默认GC),适用大部分场景。

 

 GC日志组成: GC时间, GC原因, GC位置(年轻代, 老年代, 元空间), 释放内存大小, 持续时间。

原则上, 优先看Full GC的频率, 其次查看日志STW阶段(比如G1里标记了pause, CMS里mark和remark)阶段消耗时间,

最后查看内存收缩情况。

用JMap工具可以转储Java堆到快照文件,然后用MAT工具分析,只要是堆内问题,就用MAT工具。

  • 可以详细查看Java堆内java对象占比
  • 可多维度分析堆内对象分布,查找可疑的内存最大占比的对象。
  • 可分析对象的依赖路径,查找为是否原因。

GC异常分析与定位

现象:

  • 业务偶尔出现超时现象
  • cpu负载很高

分析与解决:

  1. 超时或负载高很可能是GC异常表象,此时需要查看GC日志,结合实际业务,分析GC行为,找到GC异常根因。
  2. GC频繁,STW时间长,可能原因有:存活对象多,堆大小或堆各子区域大小划分不合理。
  3. 存活对象多,则可能存在内存泄漏,可能是代码逻辑问题,进一步可用MAT工具分析Java对象。

 

 OOM问题分析与定位

1. 根据OOM错误提示,由于配置原因导致Java堆或者Metaspace区域oom,

判断标准之一是程序是否稳定运行,实际内存占用有升有降。

2. Metaspace本身没有限制,如果设置了MaxMetaspaceSize,则受限这个配置。

3.如果内存持续增长超出业务实际可能使用的内存量,则可能存在内存泄漏。

 

内存泄漏位置判断

1. 通过异常日志提示,

eg, Java.lang.OutOfMemoryError: Java heap space,

Java.lang.OutOfMemoryError: metadata space,

Java.lang.OutOfMemoryError: Direct buffer space,

2. 开启NMT(启动参数上加上 -XX: NativeMemoryTracking=detail), 可帮助判断那块内存区域内存泄漏。

  • Java堆,即Java Heap
  • Metaspace, 即class区域
  • JVM内部
  • JNI申请的native内存

 

堆内内存泄漏排查

1. 只要是Java堆或Metaspace区域内存泄漏,都可以转储Java堆快照文件,用MAT工具分析。

2. 多维度分析堆内对象分布,查找可疑内存最大占比的对象。

3.分析对象依赖路径,查找未释放原因。

 

堆外内存泄漏排查

1. 首先通过上文手段判断内存泄漏位置。

2.根据位置对应的功能,进一步查找具体的异常代码。

3. JNI申请的native内存本身已超出JVM控制范围,没办法用JVM工具排查。

借助操作系统工具pmap排查,可查看进程内存映射,查找可疑内存。

 

案例分析

例一.

某业务发现cpu值偶尔飙升,排查发现GC停顿较长。

进一步发现大量存活对象有些异常,怀疑内存泄漏。

最后heap dump 后使用MAT分析,发现一全局HashMap持有了大量重复对象,

业务代码里移除操作由问题,实际未移除,修复后问题解决。

例二.

某业务线迁移OSGI框架后,JVM无法响应外部请求,排查GC日志发现是因为Metaspace空间不足,

频繁full gc 导致;进一步排查发现OSGI环境下,不停的defineClass, 最后发现某基础组件会不停创建,

实际上, 全局只需一份,修改后问题解决。

例三.

某业务线集群,进程RSS超出Xmx很多,开启NMT后发现确实有不受JVM控制的内存申请,

最后通过监控 malloc 监控每次请求,发现一处本地方法调用,sun.nio.fs.unitNativeDispatcher.opendir, 

进一步分析,代码中有使用Files.list函数,但没有释放动作。

 

posted @ 2022-05-05 15:40  Vincent-yuan  阅读(430)  评论(0编辑  收藏  举报