JVM 问题分析思路
1. 前言
工作中有可能遇到 java.lang.OutOfMemoryError: Java heap space 内存溢出异常, 本文提供一些内存溢出的分析及解决问题的思路.
常见异常如下:
2022-01-31 16:07:29.639 ERROR 1981 --- [http-nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: Java heap space] with root cause
java.lang.OutOfMemoryError: Java heap space
2. 内存溢出的问题
解决问题之前先来分析一下为什么会出现内存溢出的问题.
有两种可能性:
一种是应用有问题, 本该回收的内存没有进行回收导致的内存溢出, 这种情况就需要修改代码了.
第二种情况则是服务器资源不够或JVM参数设置过小导致的内存溢出,这种情况需要更换服务器或修改启动参数
我们可以使用对应的工具或命令来定位到问题, 然后分析是哪种情况, 最后再解决问题.
3. 场景模拟
通过下列代码来模拟内存溢出的情况:
// 通过无限创建自定义对象模拟内存溢出的场景
@GetMapping("oom")
public void oom(){
while(true){
CustomObj customObj = new CustomObj();
}
}
/**
* @author liuboren
* @Title: 自定义对象
* @Description: 创建该对象用于模拟OOM场景
* @date 2022/1/30 16:55
*/
public class CustomObj {
// 利用numbers成员变量尽可能更快的用光内存
private int[] numbers = new int[10000000];
}
再将应用的启动JVM参数设置为 -Xms70m -Xmx70m即可.
通过访问/oom的接口, 很快程序就会报
2022-01-31 16:07:29.639 ERROR 1981 --- [http-nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: Java heap space] with root cause
java.lang.OutOfMemoryError: Java heap space
4. 分析的方法
问题已经出来了, 我们可以通过一下几种方法来定位分析问题:
- 查看日志
- 使用jmap命令
- 分析堆转储文件
- 利用arthas进行分析
- 使用jstat命令
4.1 日志分析
通过查看对应的日志可以很清晰的定位到错误:
java.lang.OutOfMemoryError: Java heap space
at com.example.demo.entity.CustomObj.<init>(CustomObj.java:11) ~[demo.jar:0.0.1-SNAPSHOT]
at com.example.demo.controller.TestController.oom(TestController.java:36) ~[demo.jar:0.0.1-SNAPSHOT]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
可以看到TestController类中的oom方法,里面的CustomObj对象造成了内存溢出.
这时候查看对应的代码进行分析:
@GetMapping("oom")
public void oom(){
while(true){
CustomObj customObj = new CustomObj();
}
}
这个例子是我们使用了while(true) 无限的去创造对象, 所以造成的内存溢出, 我们修改对应的代码即可.
如果程序正常的情况下,就要考虑修改JVM启动参数调整堆空间或者将应用放到内存更大的服务器即可.
4.2 jmap
通过日志只可以定位到对应的代码位置,如果我们想看内存中到底是什么对象占用的空间比较多, 这时候就可以使用jmap命令了
使用下列命令可以查看内存中已产生对象的实例数和大小
jmap -histo pid |head -n 20
-histo参数代表所有的对象,包括已经垃圾回收掉的对象, 如果只想看目前存活的对象可以增加:live参数:
jmap -histo:live pid |head -n 20
至于head -n 20 则代表输出排名前20的数据, 如果不加这个参数那么展示的数据就太多了, 不利于排查问题.
然后看实际效果:
通过上图可以看出int 类型占了 40294040bytes 差不多38mb.这是因为我的测试类中的CustomObj对象 new 了一个int数组导致的.
**
* @author liuboren
* @Title: 自定义对象
* @Description: 创建该对象用于模拟OOM场景
* @date 2022/1/30 16:55
*/
public class CustomObj {
private int[] numbers = new int[10000000];
}
使用jmap命令可以快速的查看内存中的对象的实例及占用的大小, 但是缺点就是显示的不是那么直观, 并且如果应用重启了那么也就无法查看了.
所以为了避免这种情况,可以通过生成堆转储文件来进行分析.
4.3 堆转储文件分析
刚刚说了使用jmap进行内存分析的缺点, 现在看看如何使用堆转储文件
生成堆转储文件有3中方式:
- 启动时添加 JVM参数
-XX:+HeapDumpOnOutOfMemoryError参数表示当JVM发生OOM时,自动生成DUMP文件。
- 使用jmap
jmap -dump:live,format=b,file=heap.bin <pid>
- 使用arthas
heapdump
生成堆转储文件之后, 需要dump到本地进行分析
分析堆转储文件的三种方式:
- jhat
jhat -port 8000 java_pid2162.hprof
jhat默认端口是7000, 如果有端口占用的情况, 可以通过 -port 参数替换默认端口
- visualVm
JVisualVm
- Eclipse Memory Analyzer
下面看看实际的效果:
- jhat
利用jhat分析堆转储文件的可视化效果不是那么友好, 不重点介绍了, 下图是可以通过查询语句来显示大于50k的对象.
- VisualVm
执行JVisualVm命令启动客户端后, 导入堆转储文件:
显示基本的信息及执行错误的线程:
点击线程可以查看是执行的哪段代码:
对象的类型、实例数及大小
同样支持利用语句查询内存中的对象, 下面是查询内存中大于5mb的对象
可以看到VisualVm的显示界面是相当友好的, 并且功能十分的强大,可以查看是哪个线程执行的哪段代码,同时也可以查看对象的类型和大小. 推荐使用VisualVm
Eclipse Memory Analyzer
Eclipse Memory Analyzer 的功能同样很强大,就是需要额外的装一些东西, 有兴趣的朋友可以参考下面的链接 , 不多做介绍了:
链接使用对转储文件的缺点
堆转储文件的优势是展示界面友好, 并且不会因为应用重启而丢失, 但是它最大的问题就是慢, 因为随着应用的运行对转储文件的体积也在不断增加, 小则几g大则几十上百g. 无论是将文件dump到本地然还是进行分析都是非常耗时的.
4.4 arthas
Arthas 是Alibaba开源的Java诊断工具. 非常好用, 不了解的同学自行百度.
下面正文
使用arthas的 jvm和 dashboard命令 可以查看jvm的情况, 并且使用heapdump也可以生成堆转储文件
jvm命令可以看到 使用的jvm 参数 、使用的垃圾回收器、垃圾回收的时间、新生代老年代的空间、堆内存的使用情况等等
启动参数:
垃圾回收情况:
内存使用情况:
dashboard 可以看到线程执行情况及内存中各个区域的大小及使用情况:
使用heapdump命令可以生成堆转储文件
4.5 jstat
jstat也是jdk自带的小工具, 功能非常的强大,可以查看垃圾会回收的次数及时间, 查看新生代老年代的剩余空间等等.
命令如下:
jstat -gcutil pid 1000
1000是毫秒数,代表每1000毫秒输出一次
我使用jstat命令主要是查看应用的full gc的情况, 如果出现频繁的full gc 这时候就很有必要对程序进行调优了.
频繁full gc 的两个调整思路:
- 尝试调整新生代和老年代的比例, 将新生代的比例调大,这样做的原因在于动态对象年龄判定的机制(同年龄的对象的大小超过整个Survivor区的一半,大于等于这个年龄的对象都会被放入老年代)
- 尝试更换垃圾回收器(例如将cms更换为 g1)
总结
以上就是我个人的一些分析解决OOM的一些经验之谈, 如果应用发生了OOM的异常, 我们可以通过以下几个步骤尝试分析解决:
- 查看日志, 可以定位到对应的代码段, 然后进行分析是否是应用有问题, 有的话进行修改
- 通过jmap命令查看内存中的对象是什么占用的比较多,是否有需要优化的对象
- 添加对应的jvm参数可以在发生oom的时候生成堆转储文件, 然后使用对应的工具或命令来进行分析, 这样做的好处在于就算应用重启了依然有迹可循,然后解决问题
- 使用arthas进行分析. arthas不得不说非常的强大, 线上问题排查的利器. 谁用谁知道.
- 使用jstat分析gc的情况和耗时,如果有频繁的full gc,也许要进行解决
参考连接
JVM 问题分析思路的更多相关文章
- 【jvm】09-full gc分析思路
[jvm]09-full gc分析思路 欢迎关注b站账号/公众号[六边形战士夏宁],一个要把各项指标拉满的男人.该文章已在github目录收录. 屏幕前的大帅比和大漂亮如果有帮助到你的话请顺手点个赞. ...
- 全网最硬核 JVM TLAB 分析(单篇版不包含额外加菜)
今天,又是干货满满的一天.这是全网最硬核 JVM 系列的开篇,首先从 TLAB 开始.由于文章很长,每个人阅读习惯不同,所以特此拆成单篇版和多篇版 全网最硬核 JVM TLAB 分析(单篇版不包含额外 ...
- Java应用性能瓶颈分析思路
1 问题描述 因产品架构的复杂性,可能会导致性能问题的因素有很多.根据部署架构,大致的可以分为应用端瓶颈.数据库端瓶颈.环境瓶颈三大类.可以根据瓶颈的不同部位,选择相应的跟踪工具进行跟踪分析. 应用层 ...
- jvm调优思路及调优案例
jvm调优思路及调优案例 我们说jvm调优,其实就是不断测试调整jvm的运行参数,尽可能让对象都在新生代(Eden)里分配和回收,尽量别让太多对象频繁进入老年代,避免频繁对老年代进行垃圾回收,同时 ...
- CPU利用率异常的分析思路和方法交流探讨
CPU利用率异常的分析思路和方法交流探讨在生产运行当中,经常会遇到CPU利用率异常或者不符合预期的情况,此时,往往暗示着系统性能问题.那么究竟是核心应用的问题?是监控工具的问题?还是系统.硬件.网络层 ...
- JVM性能分析与优化
JVM性能分析与优化: http://www.docin.com/p-757199232.html
- enode框架step by step之框架要实现的目标的分析思路剖析1
enode框架step by step之框架要实现的目标的分析思路剖析1 enode框架系列step by step文章系列索引: 分享一个基于DDD以及事件驱动架构(EDA)的应用开发框架enode ...
- so静态分析进阶练习——一个CreakeMe的分析思路
i春秋作家:HAI_ 原文来自:https://bbs.ichunqiu.com/thread-41371-1-1.html 说明 拿到一个CreakeMe,写一个分析思路.CreakMe主要是对.s ...
- 多线程_java多线程环境下栈信息分析思路
导读:Java多线程开发给程序带来好处的同时,由于多线程程序导致的问题也越来越多,而且对问题的查找和分析解决对于菜鸟程序原来是是件头疼的事.下面我就项目中使用多线程开发程序过程中遇到的问题做详细的分析 ...
随机推荐
- idea使用教程-常用设置
[1]进入设置: [2]设置主题: [3]编辑区的字体变大或者变小: [4]鼠标悬浮在代码上有提示: [5]自动导包和优化多余的包: 手动导包:快捷键:alt+enter 自动导包和优化多余的包: [ ...
- Netty源码分析之ByteBuf引用计数
引用计数是一种常用的内存管理机制,是指将资源的被引用次数保存起来,当被引用次数变为零时就将其释放的过程.Netty在4.x版本开始使用引用计数机制进行部分对象的管理,其实现思路并不是特别复杂,它主要涉 ...
- Linux学习笔记-韩顺平
这是我学习哔哩哔哩UP主韩顺平的2021韩顺平图解Linux课程的学习笔记. 课程地址:2021韩顺平图解Linux课程 Linux基础篇-Linux目录结构 基本介绍 linux 的文件系统是采用级 ...
- Java EE数据持久化框架 • 【第1章 MyBatis入门】
全部章节 >>>> 本章目录 1.1 初识MyBatis 1.1.1 持久化技术介绍 1.1.2 MyBatis简介 1.1.2 Mybatis优点 1.1.3 利用Mav ...
- Java面向对象笔记 • 【第4章 抽象类和接口】
全部章节 >>>> 本章目录 4.1 抽象类 4.1.1 抽象方法和抽象类 4.1.2 抽象类的作用 4.1.3 实践练习 4.2 final修饰符 4.2.1 final ...
- JUC之多线程锁问题
多线程锁 8种问题锁状态: 该部分全部围绕的是以下内容并结合相应的例子:synchronized实现同步的基础:Java中每个对象都可以作为锁. 具体表现为以下三种形式:(之前只是简单的了解) 对于普 ...
- JZOJ5966. [NOIP2018TGD2T3] 保卫王国 (动态DP做法)
题目大意 这还不是人尽皆知? 有一棵树, 每个节点放军队的代价是\(a_i\), 一条边连接的两个点至少有一个要放军队, 还有\(q\)次询问, 每次规定其中的两个一定需要/不可放置军队, 问这样修改 ...
- 数三角count(归类)
评测方式:文本比较 题目描述 这是一个数三角的游戏.长度为1或SQRT(2)的小木棍放在一个网格上.如图所示,有水平的,垂直的或对角的.对角放置的木棍可以交叉. avatar 将木棍随意地放在网格上得 ...
- 02.python线性数据结构
内建常用数据类型 分类 数值型 int.float.complex.bool 序列sequence 字符串str.字节序列bytes.bytearray 列表list.元组tuple 键值对 集合se ...
- Windows 重装系统,配置 WSL,美化终端,部署 WebDAV 服务器,并备份系统分区
最新博客文章链接 最近发现我 Windows11 上的 WSL 打不开了,一直提示我虚拟化功能没有打开,但我看了下配置,发现虚拟化功能其实是开着的.然后试了各种方法,重装了好几次系统,我一个软件一个软 ...