JVM:Java常见内存溢出异常分析
转载自:http://www.importnew.com/14604.html
Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等,而Hotspot jvm的实现中,将堆内存分为了三部分,新生代,老年代,持久带,其中持久带实现了规范中规定的方法区,而内存模型中不同的部分都会出现相应的OOM错误,接下来我们就分开来讨论一下。
栈溢出(StackOverflowError)
栈溢出抛出java.lang.StackOverflowError
错误,出现此种情况是因为方法运行的时候栈的深度超过了虚拟机容许的最大深度所致。
出现这种情况,一般情况下是程序错误所致的,比如写了一个死递归,就有可能造成此种情况。 下面我们通过一段代码来模拟一下此种情况的内存溢出。
public class OOMTest{ public void stackOverFlowMethod(){
stackOverFlowMethod();
} public static void main(String... args){
OOMTest oom = new OOMTest();
oom.stackOverFlowMethod();
}
}
运行上面的代码,会抛出如下的异常:
Exception in thread "main" java.lang.StackOverflowError
at OOMTest.stackOverFlowMethod(OOMTest.java:)
堆溢出(OutOfMemoryError:java heap space)
堆内存溢出的时候,虚拟机会抛出java.lang.OutOfMemoryError:java heap space
,出现此种情况的时候,我们需要根据内存溢出的时候产生的dump文件来具体分析(需要增加-XX:+HeapDumpOnOutOfMemoryError
jvm启动参数)。出现此种问题的时候有可能是内存泄露,也有可能是内存溢出了。
如果内存泄露,我们要找出泄露的对象是怎么被GC ROOT引用起来,然后通过引用链来具体分析泄露的原因。
如果出现了内存溢出问题,这往往是程序本生需要的内存大于了我们给虚拟机配置的内存,这种情况下,我们可以采用调大-Xmx来解决这种问题。
下面我们通过如下的代码来演示一下此种情况的溢出:
1
2
3
4
5
6
7
8
9
10
|
import java.util.*; import java.lang.*; public class OOMTest{ public static void main(String... args){ List< byte []> buffer = new ArrayList< byte []>(); buffer.add( new byte [ 10 * 1024 * 1024 ]); } } |
我们通过如下的命令运行上面的代码:
java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest
程序输入如下的信息:
1
2
3
4
5
|
[GC 1180K->366K(19456K), 0.0037311 secs] [Full GC 366K->330K(19456K), 0.0098740 secs] [Full GC 330K->292K(19456K), 0.0090244 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at OOMTest.main(OOMTest.java: 7 ) |
从运行结果可以看出,JVM进行了一次Minor gc和两次的Major gc,从Major gc的输出可以看出,gc以后old区使用率为134K,而字节数组为10M,加起来大于了old generation的空间,所以抛出了异常,如果调整-Xms21M,-Xmx21M,那么就不会触发gc操作也不会出现异常了。
通过上面的实验其实也从侧面验证了一个结论:当对象大于新生代剩余内存的时候,将直接放入老年代,当老年代剩余内存还是无法放下的时候,出发垃圾收集,收集后还是不能放下就会抛出内存溢出异常了
持久带溢出(OutOfMemoryError: PermGen space)
我们知道Hotspot jvm通过持久带实现了Java虚拟机规范中的方法区,而运行时的常量池就是保存在方法区中的,因此持久带溢出有可能是运行时常量池溢出,也有可能是方法区中保存的class对象没有被及时回收掉或者class信息占用的内存超过了我们配置。当持久带溢出的时候抛出java.lang.OutOfMemoryError: PermGen space
。
我在工作可能在如下几种场景下出现此问题。
- 使用一些应用服务器的热部署的时候,我们就会遇到热部署几次以后发现内存溢出了,这种情况就是因为每次热部署的后,原来的class没有被卸载掉。
- 如果应用程序本身比较大,涉及的类库比较多,但是我们分配给持久带的内存(通过-XX:PermSize和-XX:MaxPermSize来设置)比较小的时候也可能出现此种问题。
- 一些第三方框架,比如spring,hibernate都通过字节码生成技术(比如CGLib)来实现一些增强的功能,这种情况可能需要更大的方法区来存储动态生成的Class文件。
我们知道Java中字符串常量是放在常量池中的,String.intern()这个方法运行的时候,会检查常量池中是否存和本字符串相等的对象,如果存在直接返回对常量池中对象的引用,不存在的话,先把此字符串加入常量池,然后再返回字符串的引用。那么我们就可以通过String.intern方法来模拟一下运行时常量区的溢出.下面我们通过如下的代码来模拟此种情况:
1
2
3
4
5
6
7
8
9
10
11
12
|
import java.util.*; import java.lang.*; public class OOMTest{ public static void main(String... args){ List<String> list = new ArrayList<String>(); while ( true ){ list.add(UUID.randomUUID().toString().intern()); } } } |
我们通过如下的命令运行上面代码:
java -verbose:gc -Xmn5M -Xms10M -Xmx10M -XX:MaxPermSize=1M -XX:+PrintGC OOMTest
运行后的输入如下图所示:
1
2
3
|
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at OOMTest.main(OOMTest.java: 8 ) |
通过上面的代码,我们成功模拟了运行时常量池溢出的情况,从输出中的PermGen space可以看出确实是持久带发生了溢出,这也验证了,我们前面说的Hotspot jvm通过持久带来实现方法区的说法。
OutOfMemoryError:unable to create native thread
最后我们在来看看java.lang.OutOfMemoryError:unable to create natvie thread
这种错误。 出现这种情况的时候,一般是下面两种情况导致的:
- 程序创建的线程数超过了操作系统的限制。对于Linux系统,我们可以通过ulimit -u来查看此限制。
- 给虚拟机分配的内存过大,导致创建线程的时候需要的native内存太少。我们都知道操作系统对每个进程的内存是有限制的,我们启动Jvm,相当于启动了一个进程,假如我们一个进程占用了4G的内存,那么通过下面的公式计算出来的剩余内存就是建立线程栈的时候可以用的内存。
线程栈总可用内存=4G-(-Xmx的值)- (-XX:MaxPermSize的值)- 程序计数器占用的内存
通过上面的公式我们可以看出,-Xmx 和 MaxPermSize的值越大,那么留给线程栈可用的空间就越小,在-Xss参数配置的栈容量不变的情况下,可以创建的线程数也就越小。因此如果是因为这种情况导致的unable to create native thread
,那么要么我们增大进程所占用的总内存,或者减少-Xmx或者-Xss来达到创建更多线程的目的。
JVM:Java常见内存溢出异常分析的更多相关文章
- Java常见内存溢出异常分析(OutOfMemoryError)
原文转载自:http://my.oschina.net/sunchp/blog/369412 1.背景知识 1).JVM体系结构 2).JVM运行时数据区 JVM内存结构的相关可以参考: http:/ ...
- Java虚拟机内存溢出异常--《深入理解Java虚拟机》学习笔记及个人理解(三)
Java虚拟机内存溢出异常--<深入理解Java虚拟机>学习笔记及个人理解(三) 书上P39 1. 堆内存溢出 不断地创建对象, 而且保证创建的这些对象不会被回收即可(让GC Root可达 ...
- java常见内存溢出(OOM)
jvm内存区域 程序计数器一块很小的内存空间,作用是当前线程所执行的字节码的行号指示器. java栈与程序计数器一样,java栈(虚拟机栈)也是线程私有的,其生命周期与线程相同.通常存放基本数据类型, ...
- JVM高级特性与实践(一):Java内存区域 与 内存溢出异常
套用<围城>中的一句话,“墙外面的人想进去,墙里面的人想出来”,用此来形容Java与C++之间这堵内存动态分配和垃圾收集技术所围成的“围墙”就再合适不过了. 对于从事C.C++的开发人员而 ...
- 《深入理解Java虚拟机》-----第2章 Java内存区域与内存溢出异常
2.1 概述 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任 ...
- Java内存溢出异常(上)
上一篇文章我们讲了JVM运行时数据区域与内存溢出异常,其中对于内存溢出异常这部分将的不够详细,这篇文章将着重讲解Java内存溢出异常的相关知识.如果有没看过上一篇文章的小伙伴们,请点击Java内存区域 ...
- JVM探秘:内存溢出
本系列笔记主要基于<深入理解Java虚拟机:JVM高级特性与最佳实践 第2版>,是这本书的读书笔记. 在 Java 虚拟机内存区域中,除了程序计数器外,其他几个内存区域都可能会发生OutO ...
- JVM学习与问题总结——java内存区域与内存溢出异常
java虚拟机将内存分为哪些区域? 根据Java SE7版本的Java虚拟机规范,虚拟机管理的内存包括5个运行时数据区域: 程序计数器 虚拟机栈 本地方法栈 方法区 堆 运行时数据区各部分的作用? 程 ...
- JVM(2) Java内存溢出异常
在Java虚拟机运行时数据区中,除了程序计数器之外,虚拟机栈.本地方法栈.方法区和Java堆都有发生OutOfMemoryError(简称OOM)异常的可能. 一.Java堆溢出 Java堆用于存储对 ...
随机推荐
- Yii2 组件
组件的命名空间: 响应组件: yii\web\Response Yii预定义的HTTP异常组件: yii\web\BadRequestHttpException: //状态码 400. yii\web ...
- 3ds max学习笔记(十六)-- 摄像机
摄像机添加以及应用技巧:可以更改观察的视点和空间广阔,模拟景深和运动模糊效果: 1,添加:更改观察的角度和位置,增加场景透视感: 基本操作: 景深:
- 3ds max学习笔记(十三)-- (锥化,扭曲,晶格)
锥化: 新建一个长方体,在修改器列表中选择--锥化: 参数同弯曲有些类似 参数里: 数量:控制上界面的缩放 曲线:橘黄色的线(但模型未发生变化,此时,可以回到物体身上,调整对应方向上的分段数) 组(有 ...
- js动态获取select选中的option
最近在写报表管理模块时,需要通过条件去筛选符合条件的数据,筛选条件用的布局有select,input等.在调试的过程中一直获取不到select选中的option.于是就查询些资料,发现用select的 ...
- redis:消息发布与订阅频道
1. 发布与订阅频道 消息发布与订阅像收音机与广播台的关系 1.1. publish channel message 发布频道 语法:publish channel message 作用:发布频道消息 ...
- Python二进制转十进制算法、十进转二进制算法
二进制数转换成十进制数:二进制数从右向左每位数乘以2的次方(从0开始,从右向左依次+1),然后相加求和即可 如:0101转成十进制为:1*20+0*21+1*22+0*23 =1+0+4+0=5 算法 ...
- PHP爬取历史天气
PHP爬取历史天气 PHP作为宇宙第一语言,爬虫也是非常方便,这里爬取的是从天气网获得中国城市历史天气统计结果. 程序架构 main.php <?php include_once(". ...
- 深入理解JVM(9)——类加载的过程
加载是类加载的第一步. 一.加载 a)加载的过程 1)通过一个类的全限定名获取这个类的二进制字节流,也就是class文件 2)将二进制字节流的存储结构转换为特定的数据结构,存储在方法区 3)在内存中创 ...
- 深入理解JVM(6)——JVM性能调优实战
如何在高性能服务器上进行JVM调优:以便充分利用高性能服务器的硬件资源,有两种JVM调优方案. 一. 采用64位操作系统,并为JVM分配大内存 分析:如果JVM中堆内存太小,那么就会频繁 ...
- CSS_级联和继承
2016-11-06 <CSS入门经典>第七章 1.在HTML中使用CSS样式表的三种方式: (1)内联的样式表. eg:<em style="background-whi ...