定位JVM内存溢出问题思路总结
JVM的内存溢出问题,是个常见而有时候有非常难以定位的问题。定位内存溢出问题常见方法有很多,但是其实很多情况下可供你选择的有效手段非常有限。很多方法在一些实际场景下没有实用价值。这里总结下我的一些定位思路。
要定位JVM内存溢出问题,首先要对JVM的内存布局有一定的了解,对常见的JVM内存工具要比较熟悉。所谓工欲善其事,必先利其器。而熟悉JVM的内存管理机制是你定位JVM内存问题的基石。首先介绍下JVM的内存管理机制:
JAVA程序和C类程序一个重要的区别就是JAVA中的内存回收管理工作有JVM完成,而不需要为程序中的每个new/malloc去delete/free。那JVM这么智能怎么还有内存溢出问题呢,一个常见的原因是我们的JAVA程序中长时间的持有了不该持有的对象,或者申请了过多的对象使得JVM的内存不够用。
JVM管理的几个内存区域包括以下几个内存区域:
1、 方法区:用于存储JAVA类信息、常量、静态变量。这个区域也可以发生垃圾回收,比如当一些类不在被引用时JVM可以卸载这个类,不过这种回收动作很少发生。另外所有线程都共享方法区,因此线程对方法区的访问被设计为线程安全的。
2、 虚拟机栈:JAVA虚拟机栈是线程私有的,每当启动一个新线程时,JVM都会为它分配一个JAVA虚拟机栈。没当线程调用方法时,JVM都会为虚拟机栈压入一个栈帧,该栈帧用于存储参数、局部变量、中间运算结果、方法出口等。
3、 本地方法栈:和虚拟机栈类似,只是他是专为JAVA中的Native方法服务。当线程进入本地方法后,它已经脱离的JVM的限制,甚至可以直接使用本地处理器中的寄存器。实际上本地方法的调用机制非常依赖于JVM的具体实现。
4、 堆:由JVM启动时创建,由所有线程共享,用于存放对象的实例。一般情况下它是JVM中管理的内存中最大的一块。绝大部分JVM内存问题都发生在这一块。
5、 程序计数器:同虚拟机栈一样,它也是线程私有,启动线程是创建。程序计数器的中保存的内容总是下一条将被执行的指令的地址。
这几个内存区域,除了程序计数器区域外,其他几个区域都有可能发生内存溢出问题。常见的内存溢出有两种:
1、方法区溢出。出现该问题时,JVM会报如下类似错误:java.lang.OutOfMemoryError : PermGenSpace ,Perm区的最大内存大小可以通过-XX:MaxPermSize=指定。引起这类内存溢出原因一般有两个,一个是常量池太大,一个是需要加载的CLASS类太多。出现问题的时候排查下这两种可能性,问题可以很快找到。这类问题程序稳定后也很少出现。
2、堆内存溢出。在JVM可使用的最大堆内存可以在启动的时候通过-Xmx参数指定。堆内存溢出是最为常见的内存溢出问题,发生堆内存溢出时,JVM会报告如下错误:java.lang.OutOfMemoryError : java heap space。
这里列举下在定位堆内存溢出时,常见的方法和思路。堆内存溢出顾名思义就是,堆内存不够用了,如果程序设计的最大对内存已经耗尽,那说明程序设计存在问题,不该申请很多内存的逻辑申请了很多的内存,该释放的对象没有释放。最重要的问题是就要要找出到底是什么对象没有及时释放,导致占用了过多的内存。常见的方法:
a、 一个强大的定位工具是使用 jprofiler,通过jprofiler可以实时的监控到,当前的堆内存的总体使用情况及当前存活的对象、大小、分配树、对象引用链等等,功能非常全面。如下图所示:

通过该工具还可以实时的生成堆内存快照,然后通过不同时间点生成的内存快照做比较已确定内存增长点。但是实际使用中如果程序征程运行中占用的内存就比较高,比如800M左右,而在32位机器上,JVM可以使用的最高内存在1280M左右,如果应用程序设置的最大堆内存是1024M,那么实际即将发生堆内存溢出时,程序使用的内存是处一个比较高的位置。这时候通过jprofiler监控效果就很不理想,我在windows server 2003 32位服务器上做测试,想通过jprofiler监控一个TOMCAT应用程序,该应用程序常态中占用内存在800M左右,启用jprofiler监控后,jprofiler监控程序本身就会变得很不稳定,常常莫名挂死(通过jprofiler监控应用程序的时候实际上上jprofiler对应用程序又做了一层包装后启动的,因此jprofiler监控程序挂死,等于被监控的程序挂死)。很难抓取到有用的信息。
因此jprofiler在32位机器上只适用用小内存应用程序。
b、 使用JVM自带的jmap工具导出堆信息分析,这个工具可以通过如下命令到处自定的JVM进程的堆内存:jmap –dump:format=b,file=heap.bin <pid> 。
但这个工具也有一样的问题,在应用程序使用的内存处于高位时,使用jmap导出堆信息会出现空间不足,无法导出的问题。Java自带的很多工具都有类似的限制,所以实际上你的选择并不多。
c、 在启动JVM的时候加上以下两个参数:
-XX:HeapDumpPath=./dumpfile.hprof
-XX:+HeapDumpOnOutOfMemoryError
表示出现OOM错误后,将堆信息dump出来。不过这个参数也有个问题,在实际使用的时候发现在windows平台上,如果程序是以TOMCAT服务的方式运行,出现OOM错误后堆信息也dump不出来。
d、 通过visualVM程序监控JVM,JDK 1.6中自带的可视化监控工具,这个工具比较实用,功能也比较强大。可以监控线程信息,堆内存信息,还可以实时导出堆信息以及强制GC。监控本地JVM直接启动该工具后就可以监控,远程监控需要启动jstatd和jrxml。
启动jstatd方法:新建jstatd.all.policy文件,添加如下内容,tools.jar包的路径根据实际情况修改:
grant codebase "file:/home/ndmc/tomcat/jdk/jre/lib/tools.jar" {
permission java.security.AllPermission;
};
执行启动命令./jstatd -J-Djava.security.policy=jstatd.all.policy,默认端口为1099,后面可跟-p指定其他端口号
使用jrxml远程监控JVM的时候需要加上以下参数:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8849
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.rmi.server.hostname=hostip
监控效果如下:

在监控过程中可以手动的GC和DUMP堆内存,还可以设置Heap dump on OOME: enabled,使得在堆内存溢出的时候可以dump heap。不过根据实际使用,建议最高在堆内存即将溢出的时候就应该手动dump heap,因为等到OOM的时候常常程序已经挂死,heap已经导不出来了。
e、 导出dump信息后就可以通过jprofiler工具或者HeapAnalyzer做分析。这两个工具都很强大,网上有很多的使用指导。可以初步分析出到底是什么对象在占用内存,以及相应的引用链,到这一步在结合源代码在定位内存溢出问题就相对容易了。
总结:定位内存溢出问题需要一个冷静的头脑、敏锐的观察能力、缜密的分析问题能力。甚至常常需要根据一些现象做猜测然后验证,找出最后的元凶,有可能内存的溢出是有多个方面引起的:代码BUG、软件设计问题、网络的吞吐量以及网路连接问题、数据库问题等都可能是相关需要排查的因素,甚至有时候可能是操作系统或者JVM本身存在的BUG导致。而能够在JVM堆内存即将溢出的时候导出堆信息是问题定位的关键,只要能够导出堆信息,一系列的猜测、分析是否正确就有可靠的证据。
---------------------
原文:https://blog.csdn.net/xishanxinyue/article/details/15336551
定位JVM内存溢出问题思路总结的更多相关文章
- jvm内存溢出问题的定位方法
jvm内存溢出问题的定位方法 今天给大家带来JVM体验之内存溢出问题的定位方法. 废话不多说直接开始: 一.Java堆溢出 测试代码如下: import java.util.*; public cla ...
- JVM内存溢出分析java.lang.OutOfMemoryError: Java heap space
JVM内存溢出查询java.lang.OutOfMemoryError: Java heap space查出具体原因分为几个预备步骤 1.在运行java程序是必须设置jvm -XX:+HeapDump ...
- jvm内存溢出分析
概述 jvm中除了程序计数器,其他的区域都有可能会发生内存溢出 内存溢出是什么? 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出 内存溢出和 ...
- 老李案例分享:定位JAVA内存溢出
老李案例分享:定位JAVA内存溢出 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loadrunner的培 ...
- jvm 内存溢出问题排查方法
如果你做TCP通讯或者map集合操作,并发处理等功能时,很容易出现 Java 内存溢出的问题.本篇文章,带领大家深入jvm,分析并找出jvm内存溢出的代码. jvm中除了程序计数器,其他的区域都有可能 ...
- 5种JVM垃圾收集器特点和8种JVM内存溢出原因
先来看看5种JVM垃圾收集器特点 一.常见垃圾收集器 现在常见的垃圾收集器有如下几种: 新生代收集器: Serial ParNew Parallel Scavenge 老年代收集器: Serial O ...
- Tomcat中JVM内存溢出及合理配置及maxThreads如何配置(转)
来源:http://www.tot.name/html/20150530/20150530102930.htm Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚 ...
- Tomcat中JVM内存溢出及合理配置
Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识 ...
- JVM内存溢出及合理配置
Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识 ...
随机推荐
- SpringMVC 搭建遇到的坑
1. Caused by: org.xml.sax.SAXParseException; lineNumber: 8; columnNumber: 60; cvc-complex-type.2.4.c ...
- 【转载】Oracle 中count(1) 、count(*) 和count(列名) 函数的区别
1)count(1)与count(*)比较: 1.如果你的数据表没有主键,那么count(1)比count(*)快2.如果有主键的话,那主键(联合主键)作为count的条件也比count(*)要快3. ...
- SpringBoot无法书写主启动类的情况之一
首先需要引入 spring-boot-starter-web 依赖[springboot web 项目 启动器 jar包]: 如果使用镜像请确保镜像路径正确,可参看笔者博客园m-yb的maven 安装 ...
- mysql错误集合
一.This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de 错误解决办法 这是我们开启了bin-log ...
- VC++、MFC Sqlite3数据库的使用
SQLite数据库是一种本地的轻型数据库,在存储一些本地的数据的时候,或者不需要用到Oracle,SQL2008之类的大型数据库的时候,Sqlite的优势就能够得到发挥.程序需要采集数据存储起来,可以 ...
- 提供一个Java字符串转整型数组的方法
package edu.yuliang.Data_Structure_Basics; import java.util.Scanner; public class new_string { publi ...
- python 写的员工信息查询
#!/use/bin/env pythonn#_*_ coding:utf-8 _*_import timedef Bre(): while True: Bre_falg = ra ...
- 浅入浅出JDBC————1分钟了解JDBC
一.了解基本的几个jdbc需要的类 1.1DriverManager类 DriverManager类是一个jdbc的驱动服务类.通常使用该类获得一个Connection对象,得到一个数据库的链接. 1 ...
- 《CSS揭秘》之毛玻璃效果知识点
实现代码: CodePen:毛玻璃效果 Dabblet:毛玻璃效果 HTML: <main> <blockquote> <em>"The only way ...
- Spring @Resource,@Autowired,@Qualifier的注解注入和区别
spring2.5提供了基于注解(Annotation-based)的配置,我们可以通过注解的方式来完成注入依赖.在Java代码中可以使用 @Resource或者@Autowired注解方式来经行注入 ...