原文转载自:http://my.oschina.net/sunchp/blog/369412

1.背景知识

1).JVM体系结构

2).JVM运行时数据区

JVM内存结构的相关可以参考:

http://blog.csdn.net/u012463017/article/details/49448065

2.堆溢出(OutOfMemoryError:java heap space)

堆(Heap)是Java存放对象实例的地方。

堆溢出可以分为以下两种情况,这两种情况都会抛出OutOfMemoryError:java heap space异常:

1)内存泄漏

内存泄漏是指对象实例在新建和使用完毕后,仍然被引用,没能被垃圾回收释放,一直积累,直到没有剩余内存可用。

如果内存泄露,我们要找出泄露的对象是怎么被GC ROOT引用起来,然后通过引用链来具体分析泄露的原因。

分析内存泄漏的工具有:Jprofiler,visualvm等。

示例:

import java.util.ArrayList;
import java.util.List;
import java.util.UUID; public class OOMTest {
public static void main(String[] args) {
List<UUID> list = new ArrayList<UUID>();
while (true) {
list.add(UUID.randomUUID());
}
}
}
通过如下命令运行程序:
java -Xms10M -Xmx10M -XX:-UseGCOverheadLimit OOMTest

输出结果:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at sun.security.provider.DigestBase.engineDigest(DigestBase.java:163)
at java.security.MessageDigest$Delegate.engineDigest(MessageDigest.java:576)
at java.security.MessageDigest.digest(MessageDigest.java:353)
at sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.java:226)
at java.security.SecureRandom.nextBytes(SecureRandom.java:455)
at java.util.UUID.randomUUID(UUID.java:145)
at com.demo3.Test.main(Test.java:12)
2)内存溢出

内存溢出是指当我们新建一个实力对象时,实例对象所需占用的内存空间大于堆的可用空间。

如果出现了内存溢出问题,这往往是程序本生需要的内存大于了我们给虚拟机配置的内存,这种情况下,我们可以采用调大-Xmx来解决这种问题。

示例:

import java.util.ArrayList;
import java.util.List; 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
输出结果:
[GC 836K->568K(19456K), 0.0234380 secs]
[GC 568K->536K(19456K), 0.0009309 secs]
[Full GC 536K->463K(19456K), 0.0085383 secs]
[GC 463K->463K(19456K), 0.0003160 secs]
[Full GC 463K->452K(19456K), 0.0062013 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.demo3.OOMTest.main(OOMTest.java:10)

3.持久带溢出(OutOfMemoryError: PermGen space)

持久带(PermGen space)是JVM实现方法区的地方,因此该异常主要设计到方法区和方法区中的常量池。

1).方法区

方法区(Method Area)不仅包含常量池,而且还保存了所有已加载类的元信息。当加载的类过多,方法区放不下所有已加载的元信息时,就会抛出OutOfMemoryError: PermGen
space异常。主要有以下场景:

  • 使用一些应用服务器的热部署的时候,我们就会遇到热部署几次以后发现内存溢出了,这种情况就是因为每次热部署的后,原来的class没有被卸载掉。

  • 如果应用程序本身比较大,涉及的类库比较多,但是我们分配给持久带的内存(通过-XX:PermSize和-XX:MaxPermSize来设置)比较小的时候也可能出现此种问题。

2).常量池

常量池(Runtime Constrant Pool)专门放置源代码中的符号信息。常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值外,还包含一些以文本形式出现的符号引用,比如:类和接口的全限定名;字段的名称和描述符;方法的名称和描述符等。

当常量池需要的空间大于常量池的实际空间时,也会抛出OutOfMemoryError: PermGen space异常。

例如,Java中字符串常量是放在常量池中的,String.intern()这个方法运行的时候,会检查常量池中是否存和本字符串相等的对象,如果存在直接返回对常量池中对象的引用,不存在的话,先把此字符串加入常量池,然后再返回字符串的引用。那么可以通过String.intern方法来模拟一下运行时常量区的溢出.

4.线程栈

栈(JVM Stack)存放主要是栈帧( 局部变量表, 操作数栈 , 动态链接 , 方法出口信息 )的地方。注意区分栈和栈帧:栈里包含栈帧。

与线程栈相关的内存异常有两个:

  • StackOverflowError(方法调用层次太深,内存不够新建栈帧)

  • OutOfMemoryError(线程太多,内存不够新建线程)

1).java.lang.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 com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)
at com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)
at com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)
at com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)
at com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)
at com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)
at com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)
at com.demo3.OOMTest.stackOverFlowMethod(OOMTest.java:5)
.....

2).java.lang.OutOfMemoryError:unable to create new native thread

因为虚拟机会提供一些参数来保证堆以及方法区的分配,剩下的内存基本都由栈来占有,而且每个线程都有自己独立的栈空间(堆,方法区为线程共有)。所以:

  • 如果你把虚拟机参数Xss调大了,每个线程的占用的栈空间也就变大了,那么可以建立的线程数量必然减少

  • 公式:线程栈总可用内存=JVM总内存-(-Xmx的值)- (-XX:MaxPermSize的值)- 程序计数器占用的内存

如果-Xmx或者-XX:MaxPermSize太大,那么留给线程栈可用的空间就越小,在-Xss参数配置的栈容量不变的情况下,可以创建的线程数也就越小。

上述两种情况都会导致:当创建的线程数太多时,栈内存不够用来创建新的线程,那么就会抛出java.lang.OutOfMemoryError:unable to create new native thread 异常。

PS:由于在window平台的虚拟机中,java的线程是隐射到操作系统的内核线程上的,所以运行一下产生该异常的代码时,可能会导致操作系统假死。

Java常见内存溢出异常分析(OutOfMemoryError)的更多相关文章

  1. JVM:Java常见内存溢出异常分析

    转载自:http://www.importnew.com/14604.html Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等,而Hotspot jvm的实现中,将堆 ...

  2. Java虚拟机内存溢出异常--《深入理解Java虚拟机》学习笔记及个人理解(三)

    Java虚拟机内存溢出异常--<深入理解Java虚拟机>学习笔记及个人理解(三) 书上P39 1. 堆内存溢出 不断地创建对象, 而且保证创建的这些对象不会被回收即可(让GC Root可达 ...

  3. java常见内存溢出(OOM)

    jvm内存区域 程序计数器一块很小的内存空间,作用是当前线程所执行的字节码的行号指示器. java栈与程序计数器一样,java栈(虚拟机栈)也是线程私有的,其生命周期与线程相同.通常存放基本数据类型, ...

  4. 《深入理解Java虚拟机》-----第2章 Java内存区域与内存溢出异常

    2.1 概述 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任 ...

  5. Java内存溢出异常(上)

    上一篇文章我们讲了JVM运行时数据区域与内存溢出异常,其中对于内存溢出异常这部分将的不够详细,这篇文章将着重讲解Java内存溢出异常的相关知识.如果有没看过上一篇文章的小伙伴们,请点击Java内存区域 ...

  6. JVM高级特性与实践(一):Java内存区域 与 内存溢出异常

    套用<围城>中的一句话,“墙外面的人想进去,墙里面的人想出来”,用此来形容Java与C++之间这堵内存动态分配和垃圾收集技术所围成的“围墙”就再合适不过了. 对于从事C.C++的开发人员而 ...

  7. 如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码

    程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...

  8. OutOfMemoryError/OOM/内存溢出异常实例分析--虚拟机栈和本地方法栈溢出

    关于虚拟机栈和本地方法栈,在JVM规范中描述了两种异常: 1.如果线程请求的栈深度大于JVM所允许的深度,将抛出StackOverflowError异常: 2.如果虚拟机在扩展栈时无法申请到足够的内存 ...

  9. OutOfMemoryError/OOM/内存溢出异常实例分析--堆内存溢出

    Java堆内存溢出 只要不断创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象, 那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常,代码如下: import ...

随机推荐

  1. iOS单元测试(作用及入门提升)

    由于只是一些简单实用的东西,学学还是挺不错的.其实单元测试用的好,开发起来也会快很多.单元测试对于我目前来说,就是为了方便测试一些功能是否正常运行,还有调试接口是否能正常使用.有时候你可能是为了测试某 ...

  2. Kinect for Windows V2和V1对照开发___彩色数据获取并用OpenCV2.4.10显示

    V1彩色分辨率:640x480 V2彩色分辨率:1920x1080 1,打开彩色图像帧的方式 对于V1: 使用NuiImageStreamOpen方法打开 hr = m_PNuiSensor-> ...

  3. [React] React Router: Redirect

    The Redirect component in react-router does exactly what it sounds like. It allows us to redirect fr ...

  4. 简析MFC中CString用作C字符串

      MFC中CString是一个方便的字符串操作的类, 然而很多函数需要传递字符指针, 这就需要进行CString和普通字符串的转换. 1.CString用作C字符串常量. 直接使用强制类型转换即可, ...

  5. 收集的URL

    *******************************************看文章的好地方************************************** http://www. ...

  6. C#之out与ref的共性与区别以及用法

    引入: 首先看一个例子: class Program { static void Main(string[] args) { ; int result = Test(number); Console. ...

  7. 使用Gird++打印出现“Retrieving the COM class factory for component with CLSID”的解决办法

    我们的接口需要返回一个gird++生成PDF文件的二进制数据,在本地测试都很好,发布到服务器上一直出现“Retrieving the COM class factory for component w ...

  8. XAML 名称范围

    XAML 名称范围存储 XAML 定义的对象名称和它们的对等实例之间的关系.此概念类似于其他编程语言和技术中的术语名称范围的更广泛的含义. 定义 XAML 名称范围的方式 XAML 名称范围中的名称使 ...

  9. ORA-04021

    编译或删除存储过程的时候,系统会卡住,一段时间后出现ora-04021错误. 1.可能被锁住查看v$locked select b.sid,b.serial#,b.machine,b.terminal ...

  10. 初学Java ssh之Spring 第二篇

    上一篇我们成功搭建好了spring开发的环境,接下来一起看看spring有什么神奇的地方吧(本人也是新手,有不太对的地方希望大神给指出便于修改呢,谢谢大家). 之前听说spring是在对xml文件的应 ...