HotSpot中OutOfMemoryError解析

在JVM中内存一共有3种:Heap(堆内存),Non-Heap(非堆内存) [3]和Native(本地内存)。 [1]

堆内存是运行时分配所有类实例和数组的一块内存区域。非堆内存包含方法区和JVM内部处理或优化所需的内存,存放有类结构(如运行时常量池、字段及方法结构,以及方法和构造函数代码)。本地内存是由操作系统管理的虚拟内存。当一个应用内存不足时就会抛出java.lang.OutOfMemoryError 异常。 [1]

问题 表象 诊断工具
内存不足 OutOfMemoryError Java Heap Analysis Tool(jhat) [4]
Eclipse Memory Analyzer(mat) [5]
内存泄漏 使用内存增长,频繁GC Java Monitoring and Management Console(jconsole) [6]
JVM Statistical Monitoring Tool(jstat) [7]
  一个类有大量的实例 Memory Map(jmap) - "jmap -histo" [8]
  对象被误引用 jconsole [6] 或 jmap -dump + jhat [8][4]
Finalizers 对象等待结束 jconsole [6] 或 jmap -dump + jhat [8][4]

当Java进程无法分配足够内存运行时将会抛出OutOfMemoryError:

1. java.lang.OutOfMemoryError: Java heap space

堆内存溢出时,首先判断当前最大内存是多少(参数:-Xmx 或 -XX:MaxHeapSize=),可以通过命令 jinfo -flag MaxHeapSize [9]查看运行中的JVM的配置,如果该值已经较大则应通过 mat [5] 之类的工具查找问题,或 jmap -histo [8]查找哪个或哪些类占用了比较多的内存。参数-verbose:gc(-XX:+PrintGC) -XX:+PrintGCDetails可以打印GC相关的一些数据。如果问题比较难排查也可以通过参数-XX:+HeapDumpOnOutOfMemoryError在OOM之前Dump内存数据再进行分析。此问题也可以通过histodiff打印多次内存histogram之前的差值,有助于查看哪些类过多被实例化,如果过多被实例化的类被定位到后可以通过btrace再跟踪。 [1][2]
下面代码可再现该异常: [2]

List<String> list = new ArrayList<String>();while(true) list.add(new String("Consume more memory!"));

2. java.lang.OutOfMemoryError: PermGen space

PermGen space即永久代,是非堆内存的一个区域。主要存放的数据是类结构及调用了intern()的字符串。 [2]

List<Class<?>> classes = new ArrayList<Class<?>>();while(true){    MyClassLoader cl = new MyClassLoader();    try{        classes.add(cl.loadClass("Dummy"));    }catch (ClassNotFoundException e) {        e.printStackTrace();    }}

类加载的日志可以通过btrace跟踪类的加载情况:

import com.sun.btrace.annotations.*;import static com.sun.btrace.BTraceUtils.*;

@BTracepublic class ClassLoaderDefine {

    @SuppressWarnings("rawtypes")    @OnMethod(clazz = "+java.lang.ClassLoader", method = "defineClass", location = @Location(Kind.RETURN))    public static void onClassLoaderDefine(@Return Class cl) {        println("=== java.lang.ClassLoader#defineClass ===");        println(Strings.strcat("Loaded class: ", Reflective.name(cl)));        jstack(10);    }}

除了btrace也可以打开日志加载的参数来查看加载了哪些类,可以把参数-XX:+TraceClassLoading打开,或使用参数-verbose:class-XX:+TraceClassLoading, -XX:+TraceClassUnloading),在日志输出中即可看到哪些类被加载到Java虚拟机中。该参数也可以通过jflag的命令java -jar jflagall.jar -flag +ClassVerbose动态打开-verbose:class

下面是一个使用了String.intern()的例子: [2]

List<String> list = new ArrayList<String>();int i=0;while(true) list.add(("Consume more memory!"+(i++)).intern());

你可以通过以下btrace脚本查找该类调用:

import com.sun.btrace.annotations.*;import static com.sun.btrace.BTraceUtils.*;

@BTracepublic class StringInternTrace {

    @OnMethod(clazz = "/.*/", method = "/.*/",              location = @Location(value = Kind.CALL, clazz = "java.lang.String", method = "intern"))    public static void m(@ProbeClassName String pcm, @ProbeMethodName String probeMethod,                         @TargetInstance Object instance) {        println(strcat(pcm, strcat("#", probeMethod)));        println(strcat(">>>> ", str(instance)));    }}

3. java.lang.OutOfMemoryError: unable to create new native thread

在JVM中每启动一个线程都会分配一块本地内存,用于存放线程的调用栈,该空间仅在线程结束时释放。当没有足够本地内存创建线程时就会出现该错误。通过以下代码可以很容易再现该问题: [2]

 while(true){    new Thread(new Runnable(){        public void run() {            try {                Thread.sleep(60*60*1000);            } catch(InterruptedException e) { }                }        }).start();}

4. java.lang.OutOfMemoryError: Direct buffer memory

即从Direct Memory分配内存失败,Direct Buffer对象不是分配在堆上,是在Direct Memory分配,且不被GC直接管理的空间(但Direct Buffer的Java对象是归GC管理的,只要GC回收了它的Java对象,操作系统才会释放Direct Buffer所申请的空间)。通过-XX:MaxDirectMemorySize=可以设置Direct内存的大小。 [2][10]

List<ByteBuffer> list = new ArrayList<ByteBuffer>();while(true) list.add(ByteBuffer.allocateDirect(10000000));

5. java.lang.OutOfMemoryError: GC overhead limit exceeded

JDK6新增错误类型。当GC为释放很小空间占用大量时间时抛出。一般是因为堆太小。导致异常的原因:没有足够的内存。可以通过参数-XX:-UseGCOverheadLimit关闭这个特性。 [13]

6. java.lang.OutOfMemoryError: Requested array size exceeds VM limit

详细信息表示应用申请的数组大小已经超过堆大小。如应用程序申请512M大小的数组,但堆大小只有256M,这里会抛出OutOfMemoryError,因为此时无法突破虚拟机限制分配新的数组。在大多少情况下是堆内存分配的过小,或是应用尝试分配一个超大的数组,如应用使用的算法计算了错误的大小。 [12]

7. java.lang.OutOfMemoryError: request <size> bytes for <reason>. Out of swap space?

本地内存分配失败。一个应用的Java Native Interface(JNI)代码、本地库及Java虚拟机都从本地堆分配内存分配空间。当从本地堆分配内存失败时抛出OutOfMemoryError异常。例如:当物理内存及交换分区都用完后,再次尝试从本地分配内存时也会抛出OufOfMemoryError异常。 [12]

8. java.lang.OutOfMemoryError: <reason> <stack trace> (Native method)

如果异常的详细信息是 <reason> <stack trace> (Native method) 且一个线程堆栈被打印,同时最顶端的桢是本地方法,该异常表明本地方法遇到了一个内存分配问题。与前面一种异常相比,他们的差异是内存分配失败是JNI或本地方法发现或是Java虚拟机发现。 [12]

参考资料

[1]. http://java.sun.com/developer/technicalArticles/J2SE/monitoring/
[2]. http://eyalsch.wordpress.com/2009/06/17/oome/
[3]. http://docs.oracle.com/javase/6/docs/api/java/lang/management/MemoryType.html
[4]. http://docs.oracle.com/javase/6/docs/technotes/tools/share/jhat.html
[5]. http://www.eclipse.org/mat/
[6]. http://docs.oracle.com/javase/6/docs/technotes/tools/share/jconsole.html
[7]. http://docs.oracle.com/javase/6/docs/technotes/tools/share/jstat.html
[8]. http://docs.oracle.com/javase/6/docs/technotes/tools/share/jmap.html
[9]. http://docs.oracle.com/javase/6/docs/technotes/tools/share/jinfo.html
[10]. http://eyesmore.iteye.com/blog/1133335
[11]. http://www.oracle.com/technetwork/java/javase/index-137495.html
[12]. http://www.oracle.com/technetwork/java/javase/memleaks-137499.html
[13]. http://blog.csdn.net/forandever/article/details/5717890

时间: 2024-10-11 11:48:37

HotSpot中OutOfMemoryError解析的相关文章

java中OutofMemoryError和JVM内存结构

OutOfMemoryError在开发过程中是司空见惯的,遇到这个错误,新手程序员都知道从两个方面入手来解决: 1:是排查程序是否有BUG导致内存泄漏: 2:是调整JVM启动参数增大内存. OutOfMemoryError有好几种情况,每次遇到这个错误时,观察OutOfMemoryError后面的提示信息,就可以发现不同之处,如: 引用 java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: unable t

iOS 中json解析数据出现中文乱码的问题

一般服务器的编码格式都是UTF8,这样通过json解析下来的的数据,一般中文是不会出现乱码,但是如果服务器的编码格式不是UTF8,通过json解析的数据中的中文容易出现luan乱码,怎么解决这个问题呢? 一般都是通过转码来解决,但是直接转码,是不能达到效果的,例如 [jsonStringstringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] 这样直接转码是达不到效果的. 解析数据的时候,可以先把数据存放在NSdata对象中,

C# 将XML格式字符串,写入数据集的表中 XML解析

将XML格式字符串,写入数据集的表1中 命名空间:using System.Xml; string strRead;//strRead为以下xml值 XmlDocument xd = new XmlDocument(); xd.LoadXml(strRead); XmlNodeList nodeList = xd.SelectSingleNode("root").ChildNodes;//获取bookstore节点的所有子节点 foreach (XmlNode xn in nodeLi

WCF中配置文件解析

WCF中配置文件解析 2014-06-14 参考 WCF中配置文件解析 返回 在WCF Service Configuration Editor的使用中,我们通过配置工具自动生成了WCF服务端的config文件.现在我们来看下这个配置文件各个标签的意义: 1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <!-- 这个程序集我们在项目中有引用的 --> 4

json格式数据2中方式解析实例

json格式数据2中方式解析实例 json是移动平台上非常常用的数据传输格式,本示例提供了两种Json格式文件生成与解析的方式.第一种通过JsonReader和JsonWriter两个对象进行json文件的读写操作,需要sdk在11以上.第二种通过JSONArray.JSONObject.JSONTokener等对象进行json读写操作,需要sdk在9以上.这种方式也可通过引入jar包的方式在其他版本的sdk中实现.启动客户端后,先点击写入json按钮(第1或3个按钮)生成Json文件(位置为s

Android中XML解析-Dom解析

Android中需要解析服务器端传过来的数据,由于XML是与平台无关的特性,被广泛运用于数据通信中,有的时候需要解析xml数据,格式有三种方式,分别是DOM.SAX以及PULL三种方式,本文就简单以Dom解析为例,解析XML, DOM方式解析xml是先把xml文档都读到内存中,然后再用DOM API来访问树形结构,并获取数据的,但是这样一来,如果xml文件很大,手机CPU处理能力比PC差,因此在处理效率方面就相对差了,使用Dom解析就不是太合适了. 基础维护 首先下assets目录下新建一个Bo

history.go(-1)在不同浏览器中的解析

今天遇到个问题: <a href="#" onclick="history.go(-1)">后退</a> 点击"后退"链接时,在IE,firefix下,history.go(-1)可以起到后退的作用,但在chrome下不起作用,要用history.go(-2)才生效. 后来研究了一下,发现在IE,firefix中解析为执行onclick事件,即执行history.go(-1)页面就跳走了不执行<a>标签的hre

java中dom解析xml

从xml文件中得到某个节点中value的值,条件是已知道某一个子节点的参数,如下一片段, 已知 <name> 为 “Motor hand”的值,想从整个xml文件中得到此子节点的<value>所对应的值. “<field><name>Motor hand</name><value>Right</value><type>Dotted</type></field>” 此方法是dom 遍历,获

Python的各种解析操作,和数学概念中的解析有何联系?

python中的解析 Python支持各种解析(comprehension)操作,比如列表解析.集合解析.元组解析.字典解析.它们根据某些元素来创建(推导)出一个新的列表.集合.元组.字典等.所以有的地方也称为推导,比如列表推导.集合推导等. 下面是一个列表解析的示例: 1 >>> [ i*2 for i in range(10) if i % 2 == 0 ] 2 [0, 4, 8, 12, 16] 这里是列表解析,因为使用的中括号[ xxxx ],它表示根据条件推导出一个新的列表.P