常见的溢出错误:
- java.lang.OutOfMemoryError: Java heap space
- java.lang.OutOfMemoryError: PermGen space
- java.lang.OutOfMemoryError: Requested array size exceeds VM limit
- java.lang.OutOfMemoryError: request bytes for . Out of swap space?
- java.lang.OutOfMemoryError: (Native method)
1.0 问题描述
线上有一个应用, 运行平均每天增加 100多MB的使用内存, 并且是降不下来的那种, 比如JVM配置是 3G堆内存!
但是运行一周后, 通过top命令查看到 RES使用达到了 5.2G, 并且只要持续运行就会不断地增加, 降不下来, 目前解决的结果是定时去重启!!!
2.0 排查
2.1 排查是否存在日志溢出错误
2.2 通过Jstat 命令查看垃圾回收情况
- Eden 区使用率过高: EU 的数值在第1行1347584.0, 不断快速增加, 然后在第5行进行了一次新生代中的Eden 空间GC回收, 后续然后马上又大量增加,这表明 Eden 区的内存使用率非常高。可能是由于程序创建了大量的对象,导致 Eden 区很快被填满,频繁触发年轻代垃圾回收 (但总体GC回收正常)
2.3 jmap 排查前50个最大对象
2.4 通过java诊断工具-arthas进行分析
2.4.1查看当前系统的实时数据面板
堆内存使用正常, 堆外的(nonheap)也使用不多, 远远达不到5G多, 这个时候怀疑是 JNI 异常点了
2.5 分析dump文件
也分析不出什么异常点, 占用的内存量远远达不到5g多
2.6 JVM启动参数增加 -XX:NativeMemoryTracking=detail 分析
- Java Heap (reserved=3072MB, committed=3072MB) 保留总量:3 GB , 已提交3G; (堆内存使用量在预期限制内,因为它已完全保留和提交)
- Class (reserved=1079MB, committed=61MB) 保留总量:1 GB , 已提交61MB, ,
- Thread (reserved=57MB, committed=57MB) (thread #147)线程数量和使用也是正常
- Code (reserved=250MB, committed=38MB) 代码缓存也不大
- GC (reserved=176MB, committed=176MB) GC使用也是正常的
- Internal (reserved=287MB, committed=287MB) 指代 JVM 内部使用的内存,包含各种数据结构、缓存等
2.6.1 设置比较基线
jcmd 2252351 VM.native_memory baseline
2.6.2 每天查看比较
jcmd 2089751 VM.native_memory summary.diff scale=MB
发现Internal 区域每天都会增加使用 100多MB, 直到一周后使用量到达了1G多, 已经确定问题出现在这里!!!
2.6.3 查看更为详细的内存
jcmd 2089751 VM.native_memory detail.diff scale=MB
2.7 通过arthas-profiler排查
[arthas@2089751]$ profiler execute 'start,event=doPrivileged,alluser'
从结果上看邮件找到了泄露点的调用链, 后面根据代码进行优化!!!
扩展:
Arthas 是一个强大的 Java 诊断工具,其中的 profiler
命令可以帮助开发者生成应用热点的火焰图,进行性能分析。以下是 profiler
支持的事件及其详细说明: $ profiler start --event alloc
基本事件 (Basic events)
- cpu: 采样 CPU 使用情况。
- alloc: 采样内存分配情况。
- lock: 采样锁竞争情况。
- wall: 采样墙钟时间(Wall-clock time)。
- itimer: 使用内部定时器进行采样。
Java 方法调用 (Java method calls)
可以指定特定的 Java 方法进行采样,格式如下:
- ClassName.methodName: 例如
com.example.MyClass.myMethod
。
性能事件 (Perf events)
这些事件主要用于更低层次的性能分析,涵盖了硬件和软件性能计数器等:
- page-faults: 页面错误。
- context-switches: 上下文切换。
- cycles: CPU 周期。
- instructions: 指令数。
- cache-references: 缓存引用。
- cache-misses: 缓存未命中。
- branch-instructions: 分支指令。
- branch-misses: 分支未命中。
- bus-cycles: 总线周期。
- L1-dcache-load-misses: L1 数据缓存加载未命中。
- LLC-load-misses: 最后一级缓存加载未命中。
- dTLB-load-misses: 数据 TLB 加载未命中。
- rNNN: 特定的硬件事件。
- pmu/event-descriptor/: PMU 事件描述符。
- mem:breakpoint: 内存断点。
- trace:tracepoint: 跟踪点。
- kprobe:func: 内核探针函数。
- uprobe:path: 用户探针路径。
profiler execute命令可以用来执行一些复杂的操作,支持一次执行多个子命令,命令之间用逗号分隔
操作类参数
start
:开始分析resume
:开始或恢复分析,不重置已收集的数据stop
:停止分析dump
:转储收集的数据,不停止分析会话check
:检查指定的分析事件是否可用status
:打印分析状态(不活动/运行 X 秒)meminfo
:打印分析器内存统计信息list
:显示可用的分析事件列表version[=full]
:显示代理版本
事件类参数
event=EVENT
:要跟踪的事件(cpu, wall, cache-misses 等)alloc[=BYTES]
:以 BYTES 间隔分析分配live
:仅从活动对象构建分配概况lock[=DURATION]
:分析超过 DURATION 纳秒的竞争锁
输出类参数
collapsed
:转储折叠的堆栈(FlameGraph 脚本使用的格式)flamegraph
:生成 HTML 格式的火焰图tree
:生成 HTML 格式的调用树jfr
:以 Java Flight Recorder 格式转储事件jfrsync[=CONFIG]
:使用给定的配置启动 Java Flight Recording 和分析器traces[=N]
:转储前 N 个调用跟踪flat[=N]
:转储前 N 个方法(又名 flat profile)samples
:计算样本数(默认)total
:计算总值(时间、字节等)而不是样本
其他参数
chunksize=N
:JFR chunk 的近似大小(字节),默认为 100 MBchunktime=N
:JFR chunk 的持续时间(秒),默认为 1 小时timeout=TIME
:在 TIME 时自动停止分析器(绝对或相对时间)loop=TIME
:在循环中运行分析器(连续分析)interval=N
:采样间隔(纳秒),默认为 10,000,000(即 10 毫秒)jstackdepth=N
:最大 Java 堆栈深度,默认为 2048safemode=BITS
:禁用堆栈恢复技术,默认为 0(即全部启用)file=FILENAME
:转储的输出文件名log=FILENAME
:将警告和错误记录到给定的专用流loglevel=LEVEL
:日志级别:TRACE、DEBUG、INFO、WARN、ERROR 或 NONEserver=ADDRESS
:在 ADDRESS/PORT 启动不安全的 HTTP 服务器filter=FILTER
:线程过滤器threads
:分别分析不同的线程sched
:按调度策略对线程分组cstack=MODE
:如何在 Java 堆栈之外收集 C 堆栈帧- MODE 为 ‘fp’(帧指针)、‘dwarf’、‘lbr’(最后分支记录)或 ‘no’
allkernel
:仅包括内核模式事件alluser
:仅包括用户模式事件fdtransfer
:使用 fdtransfer 将 fds 传递给分析器simple
:简单的类名而不是 FQNdot
:带点的类名sig
:打印方法签名ann
:注释 Java 方法lib
:添加库名称前缀mcache
:jmethodID 缓存的最大时间,默认为 0(禁用)include=PATTERN
:包含包含 PATTERN 的堆栈跟踪exclude=PATTERN
:排除包含 PATTERN 的堆栈跟踪begin=FUNCTION
:在执行 FUNCTION 时开始分析end=FUNCTION
:在执行 FUNCTION 时结束分析title=TITLE
:火焰图标题minwidth=PCT
:火焰图最小帧宽度百分比reverse
:生成反向堆栈的火焰图/调用树
profiler execute 'start,event=cpu'
RN`:排除包含 PATTERN 的堆栈跟踪
begin=FUNCTION
:在执行 FUNCTION 时开始分析end=FUNCTION
:在执行 FUNCTION 时结束分析title=TITLE
:火焰图标题minwidth=PCT
:火焰图最小帧宽度百分比reverse
:生成反向堆栈的火焰图/调用树
profiler execute 'start,event=cpu'
1