出处:  http://www.jianshu.com/p/cd705f88cf2a

1、内存溢出和内存泄漏的区别

  内存溢出 (Out Of Memory):是指程序在申请内存时,没有足够的内存空间供其使用,出现Out Of Memory。

  内存泄露 (Memory Leak):是指程序在申请内存后,由于某种原因无法释放已申请的内存空间,导致这块内存无法再次被利用,造成系统内存的浪费。

  memory leak会最终会导致out of memory。

  

2、内存溢出分类

2.1 栈内存溢出(StackOverflowError):

  程序所要求的栈深度过大导致,可以写一个死递归程序触发。

2.2 堆内存溢出(OutOfMemoryError : java heap space)

需要分清是 内存溢出 还是 内存泄漏:
(1)如果是内存溢出,则通过 调大 -Xms,-Xmx参数。
(2)如果是内存泄露,则看对象如何被 GC Root 引用。

2.3 持久带内存溢出(OutOfMemoryError: PermGen space)

持久带中包含方法区,方法区包含常量池。

因此持久带溢出有可能是(1) 运行时常量池溢出,也有可能是(2)方法区中保存的Class对象没有被及时回收掉或者Class信息占用的内存超过了我们配置。

用String.intern()触发常量池溢出。
Class对象未被释放,Class对象占用信息过多,有过多的Class对象。可以导致持久带内存溢出。

2.4 无法创建本地线程

Caused by: java.lang.OutOfMemoryError:unable to create new native thread

系统内存的总容量不变,堆内存、非堆内存设置过大,会导致能给线程分配的内存不足。

3、内存溢出详解

3.1 栈溢出(StackOverflowError)

  栈溢出抛出 StackOverflowError 错误,出现此种情况是因为方法运行的时候栈的深度超过了虚拟机容许的最大深度所致。一般情况下是程序错误所致的,比如写了一个死递归,就有可能造成此种情况。下面我们通过一段代码来模拟一下此种情况的内存溢出。

import java.util.*;
import java.lang.*;
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:6)

对于栈内存溢出,根据《Java 虚拟机规范》中文版:

  如果线程请求的栈容量超过栈允许的最大容量的话,Java 虚拟机将抛出一个StackOverflow异常;如果Java虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是无法申请到足够的内存去完成扩展,或者在新建立线程的时候没有足够的内存去创建对应的虚拟机栈,那么Java虚拟机将抛出一个OutOfMemory 异常。

3.2 堆溢出(OutOfMemoryError:java heap space)

堆内存溢出的时候,虚拟机会抛出 java.lang.OutOfMemoryError:java heap space。

出现此种情况的时候,我们需要根据内存溢出的时候产生的 dump 文件来具体分析(需要增加 -XX:+HeapDumpOnOutOfMemoryError jvm启动参数)。出现此种问题的时候有可能是内存泄漏,也有可能是内存溢出了。

、配置方法

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${目录}。
、参数说明

()-XX:+HeapDumpOnOutOfMemoryError参数表示当JVM发生OOM时,自动生成DUMP文件。

()-XX:HeapDumpPath=${目录}参数表示生成DUMP文件的路径,也可以指定文件名称,例如:-XX:HeapDumpPath=${目录}/java_heapdump.hprof。如果不指定文件名,默认为:java_<pid><date><time>_heapDump.hprof。

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

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

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

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

程序输出如下的信息:

[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操作也不会出现异常了。

  通过上面的实验其实也从侧面验证了一个结论:对象大于新生代剩余内存的时候,将直接放入老年代,当老年代剩余内存还是无法放下的时候,触发垃圾收集,收集后还是不能放下就会抛出内存溢出异常了。

3.3 持久带溢出(OutOfMemoryError: PermGen space)

我们知道 Hotspot jvm 通过持久带实现了Java虚拟机规范中的方法区,而运行时的常量池就是保存在方法区中。

因此持久带溢出有可能是:

(1) 运行时常量池溢出
(2)方法区中保存Class对象没有被及时回收掉或者Class信息占用的内存超过了我们配置

当持久带溢出的时候抛出 java.lang.OutOfMemoryError: PermGen space。可能在如下几种场景下出现:

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

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

  • 一些第三方框架,比如spring、hibernate都是通过字节码生成技术(比如CGLib)来实现一些增强的功能,这种情况可能需要更大的方法区来存储动态生成的Class文件。

  • 我们知道Java中字符串常量是放在常量池中的,String.intern()这个方法运行的时候,会检查常量池中是否存和本字符串相等的对象,如果存在直接返回常量池中对象的引用,不存在的话,先把此字符串加入常量池,然后再返回字符串的引用。

那么我们就可以通过String.intern方法来模拟一下运行时常量区的溢出(JDK6下,因为jdk6中常量池位于PremGen区,jdk7之后将常量池移到了Java堆区)。下面我们通过如下的代码来模拟此种情况:

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

运行后的控制台输出如下图所示:

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通过持久带来实现方法区的说法。

3.4 无法创建本地线程

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

最后我们在来看看java.lang.OutOfMemoryError:unable to create new native thread这种错误。 出现这种情况的时候,一般是下面两种情况导致的:

  • 程序创建的线程数超过了操作系统的限制。对于Linux系统,我们可以通过 ulimit -u 来查看此限制。

  • 给虚拟机分配的内存过大,导致创建线程的时候需要的native内存太少。

建立每个线程,都需要给这个线程分配栈空间。

我们都知道操作系统对每个进程的内存是有限制的,我们启动jvm,相当于启动了一个进程,假如我们一个进程占用了4G的内存,那么通过下面的公式计算出来的剩余内存就是建立线程栈的时候可以用的内存。

线程栈总可用内存 = 4G -(-Xmx的值)- (-XX:MaxPermSize的值)- 程序计数器占用的内存

通过上面的公式我们可以看出,-Xmx 和 MaxPermSize的值越大,那么留给线程栈可用的空间就越小,在-Xss参数配置的栈容量不变的情况下,可以创建的线程数也就越小。因此如果是因为这种情况导致的unable to create native thread,那么要么我们增大进程所占用的总内存,或者减少-Xmx或者-Xss来达到创建更多线程的目的。

JVM 内存溢出详解(栈溢出,堆溢出,持久代溢出、无法创建本地线程)的更多相关文章

  1. 02-java性能调优-JVM内存模型详解

    JVM整体结构与内存模型之间的关系 JVM整体结构图如下: 先贴一个代码: package com.jvm.jvmCourse2; public class Math { public static ...

  2. JVM内存结构详解

    从java编程语言说起... 1. Java编程语言简介 1.1 编程语言概述 系统级和应用级 系统级:C,C++,go,erlang 应用级:C#,Java,Python,Perl,Ruby,php ...

  3. JVM内存区域详解

    本文分为两部分:一是JVM内存区域的讲解:二是常见的内存溢出异常分析. 1.JVM内存区域 java虚拟机在执行java程序的过程中会把它管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途, ...

  4. JVM内存配置详解

    前段时间在一个项目的性能测试中又发生了一次OOM(Out of swap sapce),情形和以前网店版的那次差不多,比上次更奇怪的是,此次搞了几天之后啥都没调整系统就自动好了,死活没法再重现之前的O ...

  5. jvm内存GC详解

    一.相关概念  a. 基本回收算法 1. 引用计数(Reference Counting)  比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收 ...

  6. JVM内存配置详解(转)

    前段时间在一个项目的性能测试中又发生了一次OOM(Out of swap sapce),情形和以前网店版的那次差不多,比上次更奇怪的是,此次搞了几天之后啥都没调整系统就自动好了,死活没法再重现之前的O ...

  7. JVM内存区域详解(Eden Space、Survivor Space、Old Gen、Code Cache和Perm Gen)

    JVM区域总体分两类,heap区和非heap区.heap区又分为: Eden Space(伊甸园). Survivor Space(幸存者区). Old Gen(老年代). 非heap区又分: Cod ...

  8. java之 JVM 内存管理详解

    一.JVM结构 根据<java虚拟机规范>规定,JVM的基本结构一般如下图所示: 从左图可知,JVM主要包括四个部分: 1.类加载器(ClassLoader):在JVM启动时或者在类运行时 ...

  9. JVM内存参数详解以及配置调优

    基本概念:PermGen space:全称是Permanent Generation space.就是说是永久保存的区域,用于存放Class和Meta信息,Class在被Load的时候被放入该区域He ...

  10. 性能测试三十四:jvm内存结构(栈、堆、永久代)

    Java内存管理机制 Java采用了自动管理内存的方式Java程序是运行在Jvm之中的Java的跨平台的基于Jvm的跨平台特性内存的分配和对象的创建是在Jvm中用户可以通过一系列参数来配置Jvm Jv ...

随机推荐

  1. hdu2476(区间dp+dp)

    String painter Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  2. java——反射

    http://www.cnblogs.com/hxsyl/archive/2013/03/23/2977593.html

  3. Alpha项目冲刺! Day6-产出

    各个成员今日完成的任务 林恩:任务分工,博客撰写,了解安卓环境搭建 杨长元:安卓本地数据库 李震:了解聊天类app相关内容 胡彤:完善服务端 寇永明:研究测试代码 王浩:研究测试代码 李杰:研究测试代 ...

  4. oracle sql insert插入字符&

    最近遇到insert 语句插入&字符报弹出框,如下: sql: insert into test_ldl001 (ID, NAME) values (', '/test/test.do?act ...

  5. word excel 未响应

    前几天笔记本突然出现word 一打开就未响应的情况,导致完全无法使用.今天发现 excel 也出现了这种情况.今天终于下定决心解决这个问题. 百度上搜索了很多,找到了很多解决方案.总结如下. 一.禁用 ...

  6. 【集成模型】Boosting

    0 - 思想 Bagging算法思想是减少预测方差(variance),Boosting算法思想是为了减少预测偏差(bias). Boosting算法思想是将“弱学习算法”提升为“强学习算法”.一般来 ...

  7. spring的事务是什么?与数据库的事务是否一样

    spring的事务是什么?与数据库的事务是否一样 先说一下什么是事务,事务:是对数据库的一些列操作. 之前一直觉得事务只针对于数据库当中,5种隔离级别,7种传播行为,后来才发现这是针对Spring的, ...

  8. Qt编写控件属性设计器2-拖曳控件

    一.前言 上一篇文章把插件加载好了,并且把插件中的所有控件都显示到了列表框中,这次要做的就是实现拖曳控件的功能,用户选择一个控件拖曳到画布上,松开,在松开位置处自动实例化该控件,这个需要用到dropE ...

  9. python中os.popen, os.system()区别

    直接上个例子吧,注意结果,os.system的结果只是命令执行结果的返回值,执行成功为0: >>> a=os.system('ls') Applications Movies pyt ...

  10. 文件被sourceTree忽略了怎么办