简介
- 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负载很高
分析与解决:
- 超时或负载高很可能是GC异常表象,此时需要查看GC日志,结合实际业务,分析GC行为,找到GC异常根因。
- GC频繁,STW时间长,可能原因有:存活对象多,堆大小或堆各子区域大小划分不合理。
- 存活对象多,则可能存在内存泄漏,可能是代码逻辑问题,进一步可用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无法响应外部