文档章节

HotSpot中OutOfMemoryError解析

Yohance
 Yohance
发布于 2013/06/07 11:14
字数 1511
阅读 173
收藏 3

在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.*; @BTrace public 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.*; @BTrace public 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

本文转载自:https://code.google.com/p/hatter-source-code/wiki/Study_Java_HotSpot_OOME

共有 人打赏支持
Yohance
粉丝 14
博文 82
码字总数 44971
作品 0
浦东
程序员
私信 提问
OutOfMemoryError系列(7): Requested array size exceeds VM limit

这是本系列的第七篇文章, 相关文章列表: OutOfMemoryError系列(1): Java heap space OutOfMemoryError系列(2): GC overhead limit exceeded OutOfMemoryError系列(3): Permgen space O......

renfufei
2017/10/07
0
0
Jvm内存模型

java是在java虚拟机上运行,一般地大家讲到的Java内存其实就是Jvm内存 一、内存模型 Java内存模型,往往是指Java程序在运行时内存的模型,而Java代码是运行在Java虚拟机之上的,由Java虚拟机...

chennsh
2016/10/06
25
0
第二部分_第二章 自动内存管理机制(java内存区域与内存溢出异常)

运行时数据区域 直接内存 频繁被使用 jdk1.4加入NIO(New Input/Output),一种基于通道与缓冲区的I/O方式, 可以使用Native函数库直接分配堆外内存,通过java堆中的DirectByteBuffer对象作为...

萧小蚁
2016/09/01
34
0
Java内存 -JVM 内存管理

一.综述 如果你学过C或者C++,那么你应该感受过它们对内存那种强大的掌控力。但是强大的能力往往需要更强大的控制力才能保证能力不被滥用,如果滥用C/C++的内存管理那么很容易出现指针满天飞...

终日而思一
2018/11/19
0
0
JVM内存区域(基于jdk1.7)

一 程序计数器 程序计数器是一块较小的内存空间,可以看做当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅仅是概念模型,各种虚拟机可能通过一些更加高效的方式实现),字节码解...

sen_ye
2018/07/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

设计模式-适配器模式

一、什么是适配器? 适配器就是一种适配中间件,将两种不匹配的东西进行适配连接,举一个生活中的例子。小金最近买了最新款的macbook pro,但是发现电脑的数据接口都变成了Type c接口,这导致...

kimyeongnam
20分钟前
1
0
在没有 Emacs 的情况下使用 Org 模式

每到年初似乎总有这么一个疯狂的冲动来寻找提高生产率的方法。新年决心,正确地开始一年的冲动,以及“向前看”的态度都是这种冲动的表现。软件推荐通常都会选择闭源和专利软件。但这不是必须...

linux-tao
33分钟前
2
0
Krpano skin_settings解释

<skin_settings maps="false" 是否显示地图按钮 maps_type="google" 地图类型 maps_bing_api_key="" bing地图授权key maps_google_......

华山猛男
34分钟前
1
0
兼容率达78%!首份Android Q版本兼容性评测报告出炉

据《Android Q Beta 1版本—国内主流千款典型应用兼容性测试数据评测报告》显示,该版本兼容率为78%。 那么,千款主流应用在Android Q版本兼容情况表现如何,不兼容的主要原因是什么,又将如...

安卓绿色联盟
36分钟前
2
0
二维数组排序

以二维数组$arr中apply_num为数字降序排列:array_multisort(array_column($arr,'apply_num'),SORT_DESC,SORT_NUMERIC,$arr);...

郭周园
41分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部