`
xinklabi
  • 浏览: 1560013 次
  • 性别: Icon_minigender_1
  • 来自: 吉林
文章分类
社区版块
存档分类
最新评论

Java常见内存溢出异常分析

 
阅读更多

转自:http://www.iteye.com/news/30121

 

Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等,而Hotspot jvm的实现中,将堆内存分为了三部分,新生代,老年代,持久带,其中持久带实现了规范中规定的方法区,而内存模型中不同的部分都会出现相应的OOM错误,接下来我们就分开来讨论一下。 

栈溢出(StackOverflowError) 

栈溢出抛出java.lang.StackOverflowError错误,出现此种情况是因为方法运行的时候栈的深度超过了虚拟机容许的最大深度所致。 

出现这种情况,一般情况下是程序错误所致的,比如写了一个死递归,就有可能造成此种情况。 下面我们通过一段代码来模拟一下此种情况的内存溢出。 

Java代码 
  1. import java.util.*;  
  2. import java.lang.*;  
  3. public class OOMTest{  
  4.    
  5.   public void stackOverFlowMethod(){  
  6.       stackOverFlowMethod();  
  7.   }  
  8.    
  9.   public static void main(String... args){  
  10.       OOMTest oom = new OOMTest();  
  11.       oom.stackOverFlowMethod();  
  12.   }  
  13.    
  14. }  


运行上面的代码,会抛出如下的异常: 

引用

Exception in thread "main" java.lang.StackOverflowError 
        at OOMTest.stackOverFlowMethod(OOMTest.java:6) 


堆溢出(OutOfMemoryError:java heap space) 

堆内存溢出的时候,虚拟机会抛出java.lang.OutOfMemoryError:java heap space,出现此种情况的时候,我们需要根据内存溢出的时候产生的dump文件来具体分析(需要增加-XX:+HeapDumpOnOutOfMemoryErrorjvm启动参数)。出现此种问题的时候有可能是内存泄露,也有可能是内存溢出了。 
如果内存泄露,我们要找出泄露的对象是怎么被GC ROOT引用起来,然后通过引用链来具体分析泄露的原因。 
如果出现了内存溢出问题,这往往是程序本生需要的内存大于了我们给虚拟机配置的内存,这种情况下,我们可以采用调大-Xmx来解决这种问题。 

下面我们通过如下的代码来演示一下此种情况的溢出: 

Java代码 
  1. import java.util.*;  
  2. import java.lang.*;  
  3. public class OOMTest{  
  4.    
  5.         public static void main(String... args){  
  6.                 List<byte[]> buffer = new ArrayList<byte[]>();  
  7.                 buffer.add(new byte[10*1024*1024]);  
  8.         }  
  9.    
  10. }  


我们通过如下的命令运行上面的代码: 

Java代码 
  1. java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest  



程序输入如下的信息: 

引用

[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。 
我在工作可能在如下几种场景下出现此问题。 

1.使用一些应用服务器的热部署的时候,我们就会遇到热部署几次以后发现内存溢出了,这种情况就是因为每次热部署的后,原来的class没有被卸载掉。 
2.如果应用程序本身比较大,涉及的类库比较多,但是我们分配给持久带的内存(通过-XX:PermSize和-XX:MaxPermSize来设置)比较小的时候也可能出现此种问题。 
3.一些第三方框架,比如spring,hibernate都通过字节码生成技术(比如CGLib)来实现一些增强的功能,这种情况可能需要更大的方法区来存储动态生成的Class文件。 
我们知道Java中字符串常量是放在常量池中的,String.intern()这个方法运行的时候,会检查常量池中是否存和本字符串相等的对象,如果存在直接返回对常量池中对象的引用,不存在的话,先把此字符串加入常量池,然后再返回字符串的引用。那么我们就可以通过String.intern方法来模拟一下运行时常量区的溢出.下面我们通过如下的代码来模拟此种情况: 

Java代码 
  1. import java.util.*;  
  2. import java.lang.*;  
  3. public class OOMTest{  
  4.    
  5.         public static void main(String... args){  
  6.                 List<String> list = new ArrayList<String>();  
  7.                 while(true){  
  8.                         list.add(UUID.randomUUID().toString().intern());  
  9.                 }  
  10.         }  
  11.    
  12. }  


我们通过如下的命令运行上面代码: 
java -verbose:gc -Xmn5M -Xms10M -Xmx10M -XX:MaxPermSize=1M -XX:+PrintGC OOMTest 
运行后的输入如下图所示: 

引用

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这种错误。 出现这种情况的时候,一般是下面两种情况导致的: 

1.程序创建的线程数超过了操作系统的限制。对于Linux系统,我们可以通过ulimit -u来查看此限制。 
给虚拟机分配的内存过大,导致创建线程的时候需要的native内存太少。我们都知道操作系统对每个进程的内存是有限制的,我们启动Jvm,相当于启动了一个进程,假如我们一个进程占用了4G的内存,那么通过下面的公式计算出来的剩余内存就是建立线程栈的时候可以用的内存。 线程栈总可用内存=4G-(-Xmx的值)- (-XX:MaxPermSize的值)- 程序计数器占用的内存 通过上面的公式我们可以看出,-Xmx 和 MaxPermSize的值越大,那么留给线程栈可用的空间就越小,在-Xss参数配置的栈容量不变的情况下,可以创建的线程数也就越小。因此如果是因为这种情况导致的unable to create native thread,那么要么我们增大进程所占用的总内存,或者减少-Xmx或者-Xss来达到创建更多线程的目的。

分享到:
评论

相关推荐

    Java常见内存溢出异常分析与解决

    本篇文章主要分析了JAVA程序内存溢出问题原因,较为详细的说明了java导致程序内存溢出的原因与解决方法,感兴趣的小伙伴们可以参考一下。

    Java编程常见内存溢出异常与代码示例

    主要介绍了Java编程常见内存溢出异常与代码示例,具有一定参考价值,需要的朋友可以了解下。

    Tomcat内存溢出的三种情况及解决办法分析

    Tomcat内存溢出的三种情况及解决办法分析 Tomcat内存溢出的原因 在生产环境中tomcat内存设置不好很容易出现内存溢出。造成内存原因是不一样的,当然处理方式也不一样。 这里根据平时遇到的情况和相关资料进行一个...

    Java常见面试问题整理.docx

    线程共享:异常:OOM 内存溢出 1.Java堆:对于大多数应用来说,Java堆是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,...

    java编程常见问题

    33.java.lang.OutOfMemoryError 内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。 34.java.lang.StackOverflowError 堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出时抛出该...

    java 异常总结

    其他还有很多异常,我就不一一列举了,我要说明的是,一个合格的程序员,需要对程序中常见的问题有相当的了解和相应的解决办法,否则仅仅停留在写程序而不会改程序的话,会极大影响到自己的开发的。关于异常的全部...

    Java虚拟机

    第2章 Java内存区域与内存溢出异常 2.1 概述 2.2 运行时数据区域 2.2.1 程序计数器 2.2.2 Java虚拟机栈 2.2.3 本地方法栈 2.2.4 Java堆 2.2.5 方法区 2.2.6 运行时常量池 2.2.7 直接内存 2.3 HotSpot...

    Java进阶教程解密JVM视频教程

    * 在内存结构章节,能够学习掌握 JVM内存溢出现象,堆栈内存结构,利用内存诊断工具排查问题。彻底分析 StringTable的相关知识与性能优化,掌握直接内存分配原理和释放手段。 * 在垃圾回收章节,不仅会介绍垃圾回收...

    游戏画面就弹出内存不能为read修复工具

    解决方法:Win XP的“预读取”技术这种最佳化技术也被用到了应用程序上,系统对每一个应用程序的前几次启动情况进行分析,然后新增一个描述套用需求的虚拟“内存映像”,并把这些信息储存到WindowsPrefetch文件夹。...

    java 面试题 总结

    比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。 16、同步和异步有何异同,在什么情况下分别使用他们?举例说明。 ...

    JAVA面试题最全集

    50.JAVA语言如何进行异常处理,关键字:thorws,throw,try,catch,finally 51.Object类(或者其子类)的finalize()方法在什么情况下被调用? 52.一个“.java”原文件中是否可以包括多个类(不是内部类)? 53.掌握...

    JSTL详细标签库介绍

    &lt;BR&gt;3、常见异常实例包括:数组下标越界,算法溢出(超出数值表达范围),除数为零,无效参数、内存溢出异常处理功能:主要处理一些同步异常(除数为0),不宜处理一些异步事件(Disk I/O End、网络信息到达、点击...

    操作系统(内存管理)

    文中将为您提供如何管理内存的细节,然后将进一步展示如何手工管理内存,如何使用引用计数或者内存池来半手工地管理内存,以及如何使用垃圾收集自动管理内存。 为什么必须管理内存 内存管理是计算机编程最为基本的...

    java面试题

    答:运行时异常时(JVM)java虚拟机在运行过程中发生的问题,比如:内存溢出等问题。这类异常没法要求程序员去一一捕获并抛出,一般异常是Java类库或程序员自己写的代码发生的错误,这类异常可以由我们去一一捕获并...

    超级有影响力霸气的Java面试题大全文档

    比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。 19、同步和异步有何异同,在什么情况下分别使用他们?举例说明。 ...

    开源bbs源码java-interview-note:面试题总结

    内存溢出是怎么回事? ClassLoader有什么用? ==和equals的区别? hashCode方法的作用? Object类中有哪些方法?列举3个以上。 NIO是什么?适用于何种场景? HashMap数据结构、扩展策略,Hash冲突攻击如何防范,如何...

    进销存系统文档作业例子

    比如说内存溢出。不可能指望程序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。 16、同步和异步有何异同,在什么情况下分别使用他们?举例说明。 ...

    C#微软培训资料

    9.3 面向对象的分析 .105 9.4 面向对象的设计 .107 9.5 小 结 .110 第十章 类 .112 10.1 类 的 声 明 .112 10.2 类 的 成 员 .113 10.3 构造函数和析构函数 .119 10.4 小 结 .122 第十一章 方 法 ....

    华为编程开发规范与案例

    而pDBFat是数据库的起始地址,如果pSysHead-&gt;dbf_count值异常过大,将导致pDBFat值超过最大内存地址值,随后进行的内存操作将导致内存操作越界错误,因而在测试过程中数据库破坏后就出现了主机死机的现象。...

Global site tag (gtag.js) - Google Analytics