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 JVM

Sun自家的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内存结构的更多相关文章

  1. JAVA常见错误处理方法 和 JVM内存结构

    OutOfMemoryError在开发过程中是司空见惯的,遇到这个错误,新手程序员都知道从两个方面入手来解决:一是排查程序是否有BUG导致内存泄漏:二是调整JVM启动参数增大内存.OutOfMemor ...

  2. Java虚拟机(一):JVM内存结构

    所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemoryError的异常到底涉及到运行时数据的哪块区域?该怎么解决呢?其实如果你经常解决服务器性能问题,那么这些问 ...

  3. java线程基础巩固---多线程与JVM内存结构的关系及Thread构造函数StackSize的理解

    继续学习一下Thread的构造函数,在上次[http://www.cnblogs.com/webor2006/p/7760422.html]已经对如下构造都已经学习过了: 多线程与JVM内存结构的关系 ...

  4. java 面向对象(二):JVM内存结构

    编译完源程序以后,生成一个或多个字节码文件.我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行.意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析. <JVM规范> ...

  5. 【JVM】JVM内存结构 VS Java内存模型 VS Java对象模型

    原文:JVM内存结构 VS Java内存模型 VS Java对象模型 Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点.而且很多概念的名称看起来又那么相似,很多人会傻傻分不清 ...

  6. 区分 JVM 内存结构、 Java 内存模型 以及 Java 对象模型 三个概念

    本文由 简悦 SimpRead 转码, 原文地址 https://www.toutiao.com/i6732361325244056072/ 作者:Hollis 来源:公众号Hollis Java 作 ...

  7. Java内存模型、JVM内存结构和Java对象模型

    JVM内存结构 我们都知道,Java代码是要运行在虚拟机上的,而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途.其中有些区域随着虚拟机进程的启动而存 ...

  8. JVM内存结构、Java内存模型和Java对象模型

    Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点.而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚.比如本文要讨论的JVM内存结构.Java内存模型和Java对象模型 ...

  9. [转帖]JVM内存结构 VS Java内存模型 VS Java对象模型

    JVM内存结构 VS Java内存模型 VS Java对象模型 https://www.hollischuang.com/archives/2509 Java作为一种面向对象的,跨平台语言,其对象.内 ...

随机推荐

  1. HiveSchemaTool-Parsing failed. Reason- Unrecognized option- -dbType mysql

    版本: Hive2.1 在linux上部署Hive的时候,初始化元数据的时候,出现HiveSchemaTool:Parsing failed. Reason: Unrecognized option: ...

  2. 你不得不了解的应用容器引擎---Docker

    最近突然想搭一个redis集群玩玩,因为公司的电脑同时开2个虚拟机就卡的不行,所以我就想到用Docker开启多个redis-server来搭建.然后在网上找着找着发现,使用Docker,哪需要搭建啊, ...

  3. 【NOIP2003提高组】加分二叉树

    https://www.luogu.org/problem/show?pid=1040 令f(i,j)表示[i,j]的二叉树中最高的分数.枚举k为根,状转方程:f(i,j)=max{f(i,k-1)* ...

  4. 实践作业3:白盒测试----我是如何写测试用例DAY6

    一开始接到写白盒测试的任务,我感觉挺难的,因为感觉之前我所想到的都是黑盒测试啊,说到测试系统逻辑,感觉就有些神秘的样子没有思路了,那黑盒和白盒写的到底有啥区别.后来我请教了实验室的一个同学,他虽然还没 ...

  5. js 继承的简单易懂小例子

    js 继承 今天主要说原型链继承.构造继承.组合继承三种常用继承方式,分享一下我的理解. 原型链继承例子1 //原型继承function A(name){ this.name = name;}func ...

  6. Python爬虫(十四)_BeautifulSoup4 解析器

    CSS选择器:BeautifulSoup4 和lxml一样,Beautiful Soup也是一个HTML/XML的解析器,主要的功能也是如何解析和提取HTML/XML数据. lxml只会局部遍历,而B ...

  7. 二叉排序树(BST)构造与应用

             二叉排序树(BST)构造与应用       本文取自<数据结构与算法>(C语言版)(第三版).出版社是清华大学出版社.       本博文作为学习资料整理. 源码是VC+ ...

  8. iOS有关图片处理的总结 (四)------图片的饱和度,亮度,对照度。

    在做图片处理的时候.会遇到调节图片的饱和度的问题,这里就要用到Core Image这个框架,Core Image是一个非常强大的框架. 它能够让你简单地应用各种滤镜来处理图像,比方改动鲜艳程度, 色泽 ...

  9. 在无人值守程序(服务)中调用Microsoft Graph

    作者:陈希章 发表于 2017年5月31日 什么是无人值守程序(服务) 我在此前用了几篇文章分别介绍了在桌面应用程序(控制台),Web应用程序(ASP.NET MVC),以及PowerSehll脚本中 ...

  10. 为WebClient增加Cookie的支持

    我们经常会在应用程序中使用到WebClient模拟访问网站资源并且进行处理,如果多次访问之间我们希望为他们保存Cookie,换句话说,第一个请求产生的Cookie能自动带到第二个请求的话,可以通过自定 ...