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内存溢出问题思路总结的更多相关文章

  1. jvm内存溢出问题的定位方法

    jvm内存溢出问题的定位方法 今天给大家带来JVM体验之内存溢出问题的定位方法. 废话不多说直接开始: 一.Java堆溢出 测试代码如下: import java.util.*; public cla ...

  2. JVM内存溢出分析java.lang.OutOfMemoryError: Java heap space

    JVM内存溢出查询java.lang.OutOfMemoryError: Java heap space查出具体原因分为几个预备步骤 1.在运行java程序是必须设置jvm -XX:+HeapDump ...

  3. jvm内存溢出分析

    概述 jvm中除了程序计数器,其他的区域都有可能会发生内存溢出 内存溢出是什么? 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出 内存溢出和 ...

  4. 老李案例分享:定位JAVA内存溢出

    老李案例分享:定位JAVA内存溢出   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loadrunner的培 ...

  5. jvm 内存溢出问题排查方法

    如果你做TCP通讯或者map集合操作,并发处理等功能时,很容易出现 Java 内存溢出的问题.本篇文章,带领大家深入jvm,分析并找出jvm内存溢出的代码. jvm中除了程序计数器,其他的区域都有可能 ...

  6. 5种JVM垃圾收集器特点和8种JVM内存溢出原因

    先来看看5种JVM垃圾收集器特点 一.常见垃圾收集器 现在常见的垃圾收集器有如下几种: 新生代收集器: Serial ParNew Parallel Scavenge 老年代收集器: Serial O ...

  7. Tomcat中JVM内存溢出及合理配置及maxThreads如何配置(转)

    来源:http://www.tot.name/html/20150530/20150530102930.htm Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚 ...

  8. Tomcat中JVM内存溢出及合理配置

    Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识 ...

  9. JVM内存溢出及合理配置

    Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机.Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对Java JVM有关内存方面的知识 ...

随机推荐

  1. 第一个Spring 程序

    一 搭建好开发环境 JDK Eclipse 等 二 下载jar包 https://commons.apache.org/logging/ https://repo.spring.io/release/ ...

  2. WINDOWS SERVER 2016 设置使用照片查看器查看图片

    1.使用win+R快捷键快速打开运行,输入regedit打开注册表 2.在注册表中找到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Photo Viewe ...

  3. guava-retrying 源码解析(时间限制策略)

    一.时间限制策略相关接口和类 什么是时间限制策略呢?是指在一个时间限制内,包装任何一种重试(尝试)规则,如果超过该限制,那么这个尝试规则可能会被中断,并抛出UncheckedTimeoutExcept ...

  4. Oracle创建视图的一个问题

    问题: 在用户user1中创建视图,查询内容包含user2下的表数据, 创建视图的时候提示“权限不足”.执行如下语句: --为USER1授权 GRANT CREATE ANY TABLE TO USE ...

  5. 一轮冲刺(NABCD)和需求分析

    N我们的创意是为了解决我们测量人员在测量结束后要计算一些数据的问题,当我们观测角度后,有大量的角度需要计算,有时会用到角度与弧度的转换. A我们测量人员知道计算的公式,了解一些c++和c# B我们这个 ...

  6. 2.常用adb命令的使用

    使用电脑连接手机,查看手机的唯一编号,如果是模拟器,就是显示地址和端口号: adb devices 使用adb安装app应用: adb install apk路径和包名 -r 允许覆盖安装 -s 将a ...

  7. LINUX系统配置

    LINUX系统配置 Linux 安装jdk方法; Linux Tomcat 安装与配置 Linux redis 安装与配置 (例1) Linux redis安装配置(例2) NGINX 安装 Linu ...

  8. python import 包的路径以及相对路径加载的问题

    查看python当前系统import 命令时,系统支持的路径 除了当前目录之外,如下代码 即可查看import 包含的路径在哪些地方 参考链接 https://www.cnblogs.com/qing ...

  9. tomcat报错-->'Start Tomcat v8.0 Server at localhost' has encountered a problem.

    toncat报错-->'Start Tomcat v8.0 Server at localhost' has encountered a problem. 2016年04月16日 09:27:2 ...

  10. s21day11 python笔记

    s21day11 python笔记 一.函数小高级 函数名可以当作变量来使用 #示例一: def func(): print(123) func_list = [func, func, func] # ...