在JVM中内存一共有3种:Heap(堆内存),Non-Heap(非堆内存) [3]和Native(本地内存)。 [1] 堆内存是运行时分配所有类实例和数组的一块内存区域。非堆内存包含方法区和JVM内部处理或优化所需的内存,存放有类结构(如运行时常量池、字段及方法结构,以及方法和构造函数代码)。本地内存是由操作系统管理的虚拟内存。当一个应用内存不足时就会抛出java.lang.OutOfMemoryError 异常。 [1]
当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. java.lang.OutOfMemoryError: PermGen spacePermGen space即永久代,是非堆内存的一个区域。主要存放的数据是类结构及调用了intern()的字符串。 [2]List<Class<?>> classes =newArrayList<Class<?>>(); while(true){ MyClassLoader cl =newMyClassLoader(); try{ classes.add(cl.loadClass("Dummy")); }catch(ClassNotFoundException e){ e.printStackTrace(); } }类加载的日志可以通过btrace跟踪类的加载情况:import com.sun.btrace.annotations.*; importstatic com.sun.btrace.BTraceUtils.*; @BTrace publicclassClassLoaderDefine{ @SuppressWarnings("rawtypes") @OnMethod(clazz ="+java.lang.ClassLoader", method ="defineClass", location =@Location(Kind.RETURN)) publicstaticvoid onClassLoaderDefine(@ReturnClass 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 =newArrayList<String>(); int i=0; while(true) list.add(("Consume more memory!"+(i++)).intern());你可以通过以下btrace脚本查找该类调用:import com.sun.btrace.annotations.*; importstatic com.sun.btrace.BTraceUtils.*; @BTrace publicclassStringInternTrace{ @OnMethod(clazz ="/.*/", method ="/.*/", location =@Location(value =Kind.CALL, clazz ="java.lang.String", method ="intern")) publicstaticvoid m(@ProbeClassNameString pcm,@ProbeMethodNameString probeMethod, @TargetInstanceObject instance){ println(strcat(pcm, strcat("#", probeMethod))); println(strcat(">>>> ", str(instance))); } } 3. java.lang.OutOfMemoryError: unable to create new native thread在JVM中每启动一个线程都会分配一块本地内存,用于存放线程的调用栈,该空间仅在线程结束时释放。当没有足够本地内存创建线程时就会出现该错误。通过以下代码可以很容易再现该问题: [2]while(true){ newThread(newRunnable(){ publicvoid 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 =newArrayList<ByteBuffer>(); while(true) list.add(ByteBuffer.allocateDirect(10000000)); 5. java.lang.OutOfMemoryError: GC overhead limit exceededJDK6新增错误类型。当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/ |
- 浏览: 187157 次
- 性别:
- 来自: 深圳
最新评论
-
hayoouch:
很好,赞!!!
SSL双向认证Java实现 Tomcat篇 -
springdata:
springmvc相关demo源代码下载地址:http://w ...
在spring MVC中配置多个视图 -
冷静:
javax.net.ssl.SSLException: hos ...
SSL双向认证Java实现 Tomcat篇 -
浅绘墨漠:
你好。按照你的方法进行了试验,出现的错误就是hostname ...
SSL双向认证Java实现 Tomcat篇 -
fpplzw:
...
通过Filter实现二级域名和URLRewrite
HotSpot中OutOfMemoryError解析
- 博客分类:
- Java
相关推荐
《深入解析JDK9:C++底层源码与HotSpot虚拟机》 JDK9作为Java发展历程中的一个重要版本,引入了多项创新性改进,其中包括对HotSpot虚拟机的优化和增强。HotSpot虚拟机是Oracle JDK的核心部分,负责运行Java应用程序...
在Hotspot JVM中,本地方法栈可能与Java虚拟机栈共用同一块内存区域。 当JVM内存区域超出其限制时,可能会发生`OutOfMemoryError`。例如,如果堆内存不足,无法分配新的对象,就会抛出`OutOfMemoryError: Java heap...
本文将深入探讨HotSpot虚拟机中的内存结构,主要包括对象的创建、内存布局以及运行时数据区的各个部分。 首先,我们来看JDK7虚拟机的运行时数据区: 1. **程序计数器**:这是每个线程私有的内存区域,用于存储当前...
其具体实现细节取决于JVM,如HotSpot虚拟机中,本地方法栈与Java栈合并。同样可能出现StackOverflowError和OutOfMemoryError。 4. **堆(线程共享)** 堆是所有线程共享的内存区域,主要用来存储对象实例和数组。...
在HotSpot虚拟机中,本地方法栈和Java栈合二为一。如果栈溢出,也会抛出StackOverflowError或OutOfMemoryError异常。 4. 堆(Heap) 堆是JVM所管理的内存中最大的一块,它被所有线程共享。堆用于存储对象实例以及...
以下是对Java虚拟机相关知识点的详细解析: 1. **虚拟机类型**: - **Sun公司的Hotspot VM**:它是Java社区广泛使用的虚拟机,擅长动态优化,能够根据程序运行情况热编译字节码为机器码。 - **BEA公司的JRockit ...
书中还深入讲解了HotSpot虚拟机中对象的创建、内存布局、访问定位等细节,并通过实战案例解析了OutOfMemoryError异常的不同表现形式。 第三章专注于垃圾收集器与内存分配策略,讲解了垃圾收集的基础概念如引用计数...
永生区空间不足 JVM 规范中运行时数据区域中的方法区,在 HotSpot 虚拟机中又被习惯称为永生代或者永生区,Permanet Generation 中存放的为一些 class 的信息、常量、静态变量等数据,当系统中要加载的类、反射的类...
在 HotSpot 虚拟机中,方法区也被称作为永久代,这个方法区会发生我们常见的 java.lang.OutOfMemoryError:PermGen space 异常,我们也可以通过启动参数来控制方法区的大小:-XX:PermSize 设置最小空间-XX:...
这个内存区域是唯一一个在虚拟机中没有规定任何OutOfMemoryError情况的区域。 2. 虚拟机栈 虚拟机栈是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作...
7. OutOfMemoryError(内存溢出错误)的分析:内存溢出错误是JVM运行时常遇到的问题,文档中对此进行了分类,包括Java堆空间溢出、交换空间不足、无法创建新原生线程等情况,并提供了相应的分析方法。 8. 类加载和...
《Java虚拟机(第二版)》一书涵盖了Java开发与运行环境的核心部分——Java虚拟机(JVM)的深入解析。这本书旨在帮助读者理解JVM的工作原理,优化Java应用程序的性能,并解决运行时可能出现的问题。 Java自1995年...
在HotSpot JVM中,这部分对应于"永久代"。 2. **堆**:也是线程共享,用于存储对象实例,是垃圾收集器的主要管理区域。堆进一步细分为新生代和老年代,新生代又包含Eden空间、From Survivor空间和To Survivor空间。...
JVM通过解释器或即时编译器(如HotSpot的C1和C2编译器)将字节码转换为机器码执行。每个字节码对应一个操作,如`aload_0`表示加载局部变量表的第一个引用。 2. **类加载机制**:JVM采用“加载-验证-准备-解析-初始...
以下是对这两个问题的详细解析: 1. 类加载问题:Java 类一旦被加载到虚拟机中,如果其内容发生改变,通常不会自动重新加载。为了在不重启虚拟机的情况下加载新版本的类,我们可以自定义 ClassLoader 实现这一目标...
### Java内存的深入解析 #### 一、Java内存模型(JMM) Java内存模型(Java Memory Model,简称JMM)是Java虚拟机规范的一部分,它定义了程序中的各种变量(线程共享变量)的访问规则,以及在并发环境中变量的存储...
- 新建对象时,首先检查new指令的参数是否能在常量池中找到对应的类符号引用,并确保该类已经被加载、解析和初始化。 - 分配内存的方式有两种:指针碰撞和空闲列表。 - 在并发环境下,内存分配可能会采用CAS+失败...
2. **类加载机制**:JVM通过装载、链接(验证、准备、解析)和初始化三个阶段将类加载到内存中。类加载器分为引导类加载器、扩展类加载器和应用类加载器。 3. **字节码执行**:JVM通过解释器和即时编译器(如...