- 浏览: 123636 次
- 性别:
- 来自: 成都
-
文章分类
转自:http://apps.hi.baidu.com/share/detail/41699413
OutOfMemoryError在开发过程中是司空见惯的,遇到这个错误,新手程序员都知道从两个方面入手来解决:一是排查程序是否有BUG导致内存泄漏;二是调整JVM启动参数增大内存。OutOfMemoryError有好几种情况,每次遇到这个错误时,观察OutOfMemoryError后面的提示信息,就可以发现不同之处,如:
java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: unable to create new native thread
java.lang.OutOfMemoryError: PermGen space
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
虽然都叫OutOfMemoryError,但每种错误背后的成因是不一样的,解决方法也要视情况而定,不能一概而论。只有深入了解JVM的内存结构并仔细分析错误信息,才有可能做到对症下药,手到病除。
JVM规范JVM规范对Java运行时的内存划定了几块区域(详见这里),有:JVM栈(Java Virtual Machine Stacks)、堆(Heap)、方法区(Method Area)、常量池(Runtime Constant Pool)、本地方法栈(Native Method Stacks),但对各块区域的内存布局和地址空间却没有明确规定,而留给各JVM厂商发挥的空间。
HotSpot JVMSun自家的HotSpot JVM实现对堆内存结构有相对明确的说明。按照HotSpot JVM的实现,堆内存分为3个代:Young Generation、Old(Tenured) Generation、Permanent Generation。众所周知,GC(垃圾收集)就是发生在堆内存这三个代上面的。Young用于分配新的Java对象,其又被分为三个部分:Eden Space和两块Survivor Space(称为From和To),Old用于存放在GC过程中从Young Gen中存活下来的对象,Permanent用于存放JVM加载的class等元数据。详情参见HotSpot内存管理白皮书。堆的布局图示如下:
根据这些信息,我们可以推导出JVM规范的内存分区和HotSpot实现中内存区域的对应关系:JVM规范的Heap对应到Young和Old Generation,方法区和常量池对应到Permanent Generation。对于Stack内存,HotSpot实现也没有详细说明,但HotSpot白皮书上提到,Java线程栈是用宿主操作系统的栈和线程模型来表示的,Java方法和native方法共享相同的栈。因此,可以认为在HotSpot中,JVM栈和本地方法栈是一回事。
操作系统由于一个JVM进程首先是一个操作系统进程,因此会遵循操作系统进程地址空间的规定。32位系统的地址空间为4G,即最多表示4GB的虚拟内存。在Linux系统中,高地址的1G空间(即0xC0000000~0xFFFFFFFF)被系统内核占用,低地址的3G空间(即0×00000000~0xBFFFFFFF)为用户程序所使用(显然JVM进程运行在这3G的地址空间中)。这3G的地址空间从低到高又分为多个段;Text段用于存放程序二进制代码;Data段用于存放编译时已初始化的静态变量;BSS段用于存放未初始化的静态变量;Heap即堆,用于动态内存分配的数据结构,C语言的malloc函数申请的内存即是从此处分配的,Java的new实例化的对象也是自此分配。不同于前面三个段,Heap空间是可变的,其上界由低地址向高地址增长。内存映射区,加载的动态链接库位于这个区中;Stack即栈空间,线程的执行即是占用栈内存,栈空间也是可变的,但它是通过下界从高地址向低地址移动而增长的。详情参见这里。图示如下:
JVM本身是由native code所编写的,所以JVM进程同样具有Text/Data/BSS/Heap/MemoryMapping/Stack等内存段。而Java语言的Heap应当是建立在操作系统进程的Heap之上的,Java语言的Stack应该也是建立操作系统进程Stack之上的。 综合HotSpot的内存区域和操作系统进程的地址空间,可以大致得到下列图示:
Java线程的内存是位于JVM或操作系统的栈(Stack)空间中,不同于对象——是位于堆(Heap)中。这是很多新手程序员容易误解的地方。注意,“Java线程的内存”这个用词不是指Java.lang.Thread对象的内存,java.lang.Thread对象本身是在Heap中分配的,当调用start()方法之后,JVM会创建一个执行单元,最终会创建一个操作系统的native thread来执行,而这个执行单元或native thread是使用Stack内存空间的。
经过上述铺垫,可以得知,JVM进程的内存大致分为Heap空间和Stack空间两部分。Heap又分为Young、Old、Permanent三个代。Stack分为Java方法栈和native方法栈(不做区分),在Stack内存区中,可以创建多个线程栈,每个线程栈占据Stack区中一小部分内存,线程栈是一个LIFO数据结构,每调用一个方法,会在栈顶创建一个Frame,方法返回时,相应的Frame会从栈顶移除(通过移动栈顶指针)。在这每一部分内存中,都有可能会出现溢出错误。回到开头的OutOfMemoryError,下面逐个说明错误原因和解决方法(每个OutOfMemoryError都有可能是程序BUG导致,因此解决方法不包括对BUG的排查)。
java.lang.OutOfMemoryError: Java heap space
原因:Heap内存溢出,意味着Young和Old generation的内存不够。
解决:调整java启动参数 -Xms -Xmx 来增加Heap内存。
java.lang.OutOfMemoryError: unable to create new native thread
原因:Stack空间不足以创建额外的线程,要么是创建的线程过多,要么是Stack空间确实小了。
解决:由于JVM没有提供参数设置总的stack空间大小,但可以设置单个线程栈的大小;而系统的用户空间一共是3G,除了Text/Data/BSS/MemoryMapping几个段之外,Heap和Stack空间的总量有限,是此消彼长的。因此遇到这个错误,可以通过两个途径解决:1.通过-Xss启动参数减少单个线程栈大小,这样便能开更多线程(当然不能太小,太小会出现StackOverflowError);2.通过-Xms -Xmx 两参数减少Heap大小,将内存让给Stack(前提是保证Heap空间够用)。
java.lang.OutOfMemoryError: PermGen space
原因:Permanent Generation空间不足,不能加载额外的类。
解决:调整-XX:PermSize= -XX:MaxPermSize= 两个参数来增大PermGen内存。一般情况下,这两个参数不要手动设置,只要设置-Xmx足够大即可,JVM会自行选择合适的PermGen大小。
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
原因:这个错误比较少见(试着new一个长度1亿的数组看看),同样是由于Heap空间不足。如果需要new一个如此之大的数组,程序逻辑多半是不合理的。
解决:修改程序逻辑吧。或者也可以通过-Xmx来增大堆内存。
在GC花费了大量时间,却仅回收了少量内存时,也会报出OutOfMemoryError,我只遇到过一两次。当使用-XX:+UseParallelGC或-XX:+UseConcMarkSweepGC收集器时,在上述情况下会报错,在HotSpot GC Turning文档上有说明:
The parallel(concurrent) collector will throw an OutOfMemoryError if too much time is being spent in garbage collection: if more than 98% of the total time is spent in garbage collection and less than 2% of the heap is recovered, an OutOfMemoryError will be thrown.
对这个问题,一是需要进行GC turning,二是需要优化程序逻辑。
java.lang.StackOverflowError
原因:这也内存溢出错误的一种,即线程栈的溢出,要么是方法调用层次过多(比如存在无限递归调用),要么是线程栈太小。
解决:优化程序设计,减少方法调用层次;调整-Xss参数增加线程栈大小。
在解决java内存溢出问题之前,需要对jvm(java虚拟机)的内存管理有一定的认识。jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)、Heap space(堆区域)、Java Stacks(Java栈)。其中永久保存区域主要存放Class(类)和Meta的信息,Class第一次被Load的时候被放入PermGen space区域,Class需要存储的内容主要包括方法和静态属性。堆区域用来存放Class的实例(即对象),对象需要存储的内容主要是非静态属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被jvm的垃圾回收机制管理。而Java栈跟大多数编程语言包括汇编语言的栈功能相似,主要基本类型变量以及方法的输入输出参数。Java程序的每个线程中都有一个独立的堆栈。容易发生内存溢出问题的内存空间包括:Permanent Generation space和Heap space。
第一种OutOfMemoryError: PermGen space
发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。解决这类问题有以下两种办法:
1. 增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。如针对tomcat6.0,在catalina.sh 或catalina.bat文件中一系列环境变量名说明结束处(大约在70行左右) 增加一行:
JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m"
如果是windows服务器还可以在系统环境变量中设置。感觉用tomcat发布sprint+struts+hibernate架构的程序时很容易发生这种内存溢出错误。使用上述方法,我成功解决了部署ssh项目的tomcat服务器经常宕机的问题。
2. 清理应用程序中web-inf/lib下的jar,如果tomcat部署了多个应用,很多应用都使用了相同的jar,可以将共同的jar移到tomcat共同的lib下,减少类的重复加载。这种方法是网上部分人推荐的,我没试过,但感觉减少不了太大的空间,最靠谱的还是第一种方法。
第二种OutOfMemoryError: Java heap space
发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。解决这类问题有两种思路:
1. 检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。
我以前写一个使用K-Means文本聚类算法对几万条文本记录(每条记录的特征向量大约10来个)进行文本聚类时,由于程序细节上有问题,就导致了Java heap space的内存溢出问题,后来通过修改程序得到了解决。
2. 增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m
第三种OutOfMemoryError:unable to create new native thread
这种错误在Java线程个数很多的情况下容易发生,我暂时还没遇到过,发生原意和解决办法可以参考:http://hi.baidu.com/hexiong/blog/item/16dc9e518fb10c2542a75b3c.html
发表评论
-
eclipselink-DDL Schema Generation的四种方式
2016-10-12 00:17 591persistence.xml文件配置: < ... -
Java开发中的23种设计模式
2016-09-28 00:40 575设计模式(Design Patterns) ... -
ManyToOne 双向一对多关系
2016-08-05 01:38 530双向一对多关系,一是关系维护端(owner side),多是 ... -
理解RESTful架构
2016-06-22 00:33 581原文:http://www.ruanyifen ... -
foreach循环
2016-05-31 22:23 497这种有冒号的for循环叫做foreach循环,foreach语 ... -
java几种常用设计模式简单示例
2016-05-19 23:02 539PART A:前言 平常我们都在敲代码,为了要实现一些我们 ... -
iText PdfPCell内容水平垂直居中
2016-01-14 00:13 11913先调用Cell.setUseAscender(true);再调 ... -
深入理解 hash 函数、HashMap
2015-12-15 00:52 656http://www.2cto.com/kf/201409/3 ... -
iText的showTextAligned方法
2015-12-06 16:47 5552java使用itext的showTextAligned方法给 ... -
iText PdfTemplate的使用
2015-12-06 02:32 1442在开发系统时,需要在PDF上写入总页数。于是在网上搜索到 ... -
iText表格 分页
2015-11-30 23:31 5260前言 在上一节中,通过listing 4.16产生的表格拥 ... -
iText生成PDF文档部分页面横置
2015-11-27 02:02 5578整个PDF文档页面设置 Rectangle rect ... -
iText生成PDF格式设置
2015-11-27 00:52 2560import java.io.ByteArrayOutputS ... -
Spring中的IOC和AOP
2015-11-19 00:47 523IOC,依赖倒置的意思,所谓依赖,从程序的角度看,就是比如A要 ... -
【转载】纯Java获得本地MAC地址
2015-07-29 21:18 6111 import java.net.*; 2 3 clas ... -
Java笔试题
2014-05-25 21:53 5831. float型float f=3.4是否正 ... -
单例模式的常见应用场景
2014-05-25 21:30 882单例模式(Singleton)也叫单态模式,是设计模式中最 ... -
深入Java单例模式
2014-05-25 21:29 591在GoF的23种设计模式中,单例模式是比较简单的一种。然而 ... -
java 异常捕捉 ( try catch finally )
2014-05-25 21:15 590前言:java 中的异常处理机制你真的理解了吗?掌握了吗?c ... -
多线程的实现
2014-05-15 02:11 531http://www.cnblogs.com/rollenho ...
相关推荐
1. **JVM内存结构** JVM内存分为几个关键区域:方法区(Method Area)、堆(Heap)、栈(Stack)、程序计数器(PC Register)和本地方法栈(Native Method Stack)。每个区域都有特定的用途: - **方法区**:存储...
理解JVM内存结构和内存分配机制对于避免内存溢出(OutOfMemoryError)、提升程序性能、减少垃圾回收开销至关重要。开发者应关注内存配置、对象生命周期管理以及适当的垃圾回收策略,以优化应用程序的性能和稳定性。
### JVM内存空间分配详解 #### 一、JVM内存模型概览 ...综上所述,理解JVM内存分配机制对于Java开发者来说至关重要,这不仅有助于编写高效、稳定的代码,还能在遇到性能瓶颈时快速定位问题并进行优化。
在这份由Sun Microsystems公司出版的《JVM内存管理白皮书》中,我们可以找到关于Java虚拟机(JVM)内存管理的详细介绍和深入分析。这份文档对于想要深入了解JVM工作原理的读者来说是一份宝贵的学习资料。在这份...
1. **内存模型**:JVM内存分为堆内存(Heap)、栈内存(Stack)、方法区(Method Area)、程序计数器(PC Register)和本地方法栈(Native Method Stack)。堆是存储对象实例的主要区域,栈则用于存储方法调用时的...
在进行JVM内存配置优化之前,我们需要了解Java虚拟机(JVM)的内存结构。Java的逻辑内存模型大致分为几个部分: 1. **堆内存(Heap)**:主要用于存储对象实例、数组等引用类型的数据。在Java中使用`new`关键字创建的...
在Java程序中,JVM内存是一个重要组成部分,它管理着对象的创建、存储和回收等过程。理解JVM内存分配对于编写高效的Java应用程序至关重要。 描述部分指出,本文将探讨JVM如何使用Windows和Linux平台上的本机内存。...
了解JVM内存结构和调优技术,可以帮助我们编写出更高效、更稳定的Java应用程序。在实际工作中,结合监控工具(如VisualVM、JConsole、JMX等)进行分析,可以更好地理解应用程序的内存行为并进行针对性优化。同时,...
- `java.lang.OutOfMemoryError: Java heap space`表示Java程序在运行过程中耗尽了所有可用的堆内存空间。 - 当程序创建新的对象或者分配内存时,如果无法在现有的堆内存中找到足够的连续空闲内存块,则会抛出此...
这类异常通常表明Java虚拟机(JVM)在运行过程中遇到了内存不足的问题,特别是当堆内存无法满足程序的需求时。 #### 二、原因分析 1. **项目规模过大**:如果项目中的类文件数量过多或者单个对象占用内存较大时,...
了解JVM内存区域的划分及其工作原理对于Java开发者来说至关重要,这有助于理解和解决性能问题,如内存泄漏和栈溢出。在实际开发中,合理设置JVM参数(如-Xms, -Xmx等)可以有效防止内存错误,同时,通过阅读如《深入...
了解JVM内存结构是理解内存溢出的关键。 #### 二、JVM运行时数据区域 - **程序计数器(Program Counter Register)**:当前线程所执行的字节码的行号指示器。每条线程拥有独立的程序计数器,因此它属于线程私有区域...
了解JVM内存结构有助于我们理解Java应用程序的内存分配和管理,从而进行更有效的内存优化,减少内存溢出和栈溢出等问题。例如,通过调整堆大小、优化对象生命周期、合理使用静态变量等策略,可以提高应用的性能和...
Java中的“内存不足OOM (Out Of Memory):java.lang.OutOfMemoryError”是一个常见的运行时错误,它表示Java虚拟机(JVM)在尝试分配新的对象或数据结构时,发现系统内存不足以完成此操作。这个问题通常发生在程序...
### JVM内存详解:深入探索本机内存的奥秘 #### 1. 本机内存概览 本机内存,即Native Memory,是JVM运行时所依赖的...以上资料提供了更深层次的技术细节和实践指导,有助于读者进一步提升对JVM内存管理的理解和技能。
### Java内存模型与内存结构详解 #### 一、引言 在深入了解Java内存模型与内存结构之前,我们首先需要明确这两个概念的区别与联系。简单来说,“内存结构”侧重于描述虚拟机执行Java程序时如何组织内存空间;而...
1. **JVM内存结构**: JVM内存主要分为五个区域:方法区(Method Area)、堆(Heap)、虚拟机栈(Java Virtual Machine Stack)、本地方法栈(Native Method Stack)和程序计数器(Program Counter Register)。 2...
JVM内存区域划分是Java虚拟机(JVM)中的一种内存管理机制,主要分为五个部分:程序计数器、Java栈、本地方法栈、堆和方法区。这些区域各自扮演着不同的角色,共同组成了JVM的内存管理系统。 程序计数器 程序...
2. **内存管理**:JVM内存主要分为堆内存和栈内存,其中堆内存用于对象实例的存储,栈内存则处理方法调用。了解内存分配、GC(Garbage Collection)策略以及内存泄漏的检测和预防对于避免程序运行缓慢至关重要。 3....