本篇文章理解源自于《深入理解java虚拟机》2.4章节 实战:OutOfMemoryError异常
 
在以下例子中,所有代码都可以抛出OutOfMemoryError异常,但是要区分到底是内存泄漏(Memory Leak)还是内存溢出(Memory Overflow),我们需要借助Eclipse Memory Analyzer(也成为MAT,mat是一个分析Java内存的神器)插件来分析.hprof文件才能得知。
安装Eclipse Memory Analyzer插件方法:eclipse -> Help -> Eclipse Marketplace -> search框搜索“Memory Analyzer” -> 点击install即可安装。
IBM官方介绍Eclipse Memory Analyzer网址:https://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html?ca=drs-
 
在测试以下例子之前,我们来了解下VM参数是个什么东西:
VM arguments参数解析
-verbose:gc 参数表示将在控制台输出full GC详细,例如:[Full GC 168K->97K(1984K), 0.0253873 secs]
-Xms20M 初始堆大小。默认值:物理内存的1/64(<1GB)。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制。
-Xmx20M 最大堆大小。默认值:物理内存的1/4(<1GB)。默认空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。
-Xmn10M 年轻代大小。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-XX:+PrintGCDetails 打印full gc的详细信息。
-XX:+HeapDumpOnOutOfMemoryError JVM 就会在发生内存泄露时抓拍下当时的内存状态,也就是我们想要的堆转储文件。也就是会在项目根目录下生成*.hprof文件可供分析。
-XX:+HeapDumpOnCtrlBreak 如果你不想等到发生崩溃性的错误时才获得堆转储文件,也可以通过设置如下 JVM 参数来按需获取堆转储文件。
-XX:SurvivorRatio=8 Eden区与Survivor区的大小比值。设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。
VM参数参考该文章:JVM系列三:JVM参数设置、分析(http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html
 
new一个Junit4单元测试类VirtualTest,依次放入以下例子代码:
例子1
/**
* 以Run AS -> Run Configurations运行。
* 并设置 VM arguments参数如下:
* -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError
*/
@Test
public void testOutOfMemoryError() {
  List<VirtualTest> list = new ArrayList<VirtualTest>();
  while(true){
    list.add(new VirtualTest());
  }
}
执行代码后,控制台抛出异常如下:
[GC (Allocation Failure) [PSYoungGen: 8192K->1018K(9216K)] 8192K->2211K(19456K), 0.0078156 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 9210K->1000K(9216K)] 10403K->7720K(19456K), 0.0356040 secs] [Times: user=0.11 sys=0.00, real=0.04 secs]
…………
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid10604.hprof ...
Heap dump file created [29234562 bytes in 0.214 secs]
Heap
PSYoungGen total 9216K, used 8154K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 99% used [0x00000000ff600000,0x00000000ffdf6a10,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 9292K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 90% used [0x00000000fec00000,0x00000000ff513390,0x00000000ff600000)
Metaspace used 4979K, capacity 5182K, committed 5248K, reserved 1056768K
class space used 591K, capacity 626K, committed 640K, reserved 1048576K
异常分析
这个异常我们要借助Memory Analyzer工具来查看内存溢出的是哪个对象,刷新整个项目,点击根目录下的java_pid10604.hprof,默认是Overview视图,上面是一个显眼的饼图:
 
在这个饼图里面,我们可以看到有一个可疑对象在总共16.4MB的内存里,它占用了15.5MB的内存,占用了94.5%的内存。
在饼图的左下方,这个报告告诉我们这个内存溢出发生在main这个线程里面。
接下来我们当然想知道到底是哪个对象干的,那么找到饼图的下方还有一些工具:
点击Histogram链接,打开Histogram视图:
我们发现在Objets这一栏,最多的一个对象实例竟然达到810327个,这个对象类是com.virtual.VirtualTest。那么很清楚了,我们new出了太多这个类对象,导致垃圾内存溢出。
在dominator_tree视图中,可以看到main线程占用了94.02%的内存
在上图中,我们可以看到该.hprof文件存放的路径、大小、生成时间、格式、报告中总共产生多少对象,堆大小(16.4MB)等信息。
 
例子2
  /**
* 以Run AS -> Run Configurations运行。
* 并设置 VM arguments参数如下:
* -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError
* 注意:-XX:+HeapDumpOnOutOfMemoryError参数将会使项目根目录下产生java_*.hprof文件
*/
@Test
public void testOutOfMemory2(){
List<String> list = new ArrayList<String>();
for(int i=0;i<10000000;i++){
String str = new String();
list.add(str);
}
}
执行代码后,控制台抛出异常如下
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to java_pid3360.hprof ...
Heap dump file created [12525143 bytes in 0.136 secs]
异常分析
Sun 官方对此的定义是:“并行/并发回收器在GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。用来避免内存过小造成应用不能正常工作。“
意思就是GC用尽了全力可还是来不及回收你new出来的那么多string对象,而且list对象越来越大,你还往里面塞对象,还让它活着,你还不让我回收,于是……呵呵……我挂了(>﹏<)
但是我抛出这个异常的目的就是在JVM彻底死了之前告诉你我挂了,好让你来得及实施一些紧急措施,比如紧急保存之类。
参考文章《java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得》http://www.cnblogs.com/hucn/p/3572384.html
 
总结
到目前为止,我还没能从Eclipse Memory Analyzer工具中发现能够区分出java内存泄漏(Memory Leak)和内存溢出(Memory Overflow)这两种状态的直接证据。
个人理解为:
java内存泄漏(Memory Leak) 当new出很多对象,而GC无法及时回收这些对象时,会导致内存泄漏。
内存溢出(Memory Overflow) 当一个对象长时间存活,且占用内存巨大直接威胁到总内存时,会导致内存溢出。
 
以下是其它已在JDK1.8版本中不再报内存溢出的代码:
举例1
  /**
* 在JDK1.8中不会抛出异常!!!!
* 以Run AS -> Run Configurations运行。 并设置 VM arguments参数如下:
* -Xss128k -XX:+HeapDumpOnOutOfMemoryError
* 注意:-XX:+HeapDumpOnOutOfMemoryError参数将会使项目根目录下产生java_*.hprof文件
*/
@Test
public void testOutOfMemory_noError() {
int stackLength = 1;
try {
while (true) {
stackLength = stackLength + 1;
System.out.println("stack length ==" + stackLength);
}
} catch (Throwable t) {
System.out.println("stack length : " + stackLength);
throw t;
}
}
举例2
  /**
* 在JDK1.8中不会抛出异常!!!! 但是会使操作系统假死。
* 以Run AS -> Run Configurations运行。 并设置 VM arguments参数如下:
* -Xss2M -XX:+HeapDumpOnOutOfMemoryError
* 注意:-XX:+HeapDumpOnOutOfMemoryError参数将会使项目根目录下产生java_*.hprof文件
*/
@Test
public void testOutOfMemory_noError2() {
while (true) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
}
}
});
t.start();
}
}
举例3
  /**
* 在JDK1.8中不会抛出异常!!!!
* 以Run AS -> Run Configurations运行。 并设置 VM arguments参数如下:
* -XX:PermSize=10M -XX:MaxPermSize=10M -XX:+HeapDumpOnOutOfMemoryError
* 注意:-XX:+HeapDumpOnOutOfMemoryError参数将会使项目根目录下产生java_*.hprof文件
*/
@Test
public void testOutOfMemory_noError3() {
List<String> list = new ArrayList<String>();
int i=0;
while (true) {
list.add(String.valueOf(i));
}
}
举例4
  /**
* 在JDK1.8中不会抛出异常!!!!
* 以Run AS -> Run Configurations运行。 并设置 VM arguments参数如下: -Xms10m -Xmx10m
* -XX:PermSize=10M -XX:MaxPermSize=10M -XX:+HeapDumpOnOutOfMemoryError
* 注意:-XX:+HeapDumpOnOutOfMemoryError参数将会使项目根目录下产生java_*.hprof文件
*/
@Test
public void testOutOfMemory_noError4() {
while(true){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.getClass());
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() { @Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
return proxy.invoke(obj, args);
}
});
enhancer.create();
}
}
 
 
 
 

OutOfMemoryError异常java内存泄漏(Memory Leak)和内存溢出(Memory Overflow)的更多相关文章

  1. Memory Leak(内存泄漏)问题总结(转)

    最近听了一些关于Memory Leak(内存泄漏)的seminar,感觉有些收获,所以留个记录,并share给朋友. 1 什么是Memory Leak. Memory Leak是指由于错误或不完备的代 ...

  2. 堆(heap)和栈(stack)、内存泄漏(memory leak)和内存溢出

    来源:http://blog.itpub.net/8797129/viewspace-693648/ 简单的可以理解为:heap:是由malloc之类函数分配的空间所在地.地址是由低向高增长的.sta ...

  3. 安卓android WebView Memory Leak WebView内存泄漏

    Android WebView Memory Leak WebView内存泄漏 在这次开发过程中,需要用到webview展示一些界面,但是加载的页面如果有很多图片就会发现内存占用暴涨,并且在退出该界面 ...

  4. 【内存泄漏】 C/C++内存泄漏及其检测工具

    对于一个c/c++程序员来说,内存泄漏是一个常见的也是令人头疼的问题.已经有许多技术被研究出来以应对这个问题,比如 Smart Pointer,Garbage Collection等.Smart Po ...

  5. Android WebView Memory Leak WebView内存泄漏

    在这次开发过程中,需要用到webview展示一些界面,但是加载的页面如果有很多图片就会发现内存占用暴涨,并且在退出该界面后,即使在包含该webview的Activity的destroy()方法中,使用 ...

  6. 什么是内存泄漏?(What is a memory leak?)

    程序中的内存泄漏是怎么回事呢? 我们写过很多带有关键词free()的程序.比如我在这篇博文关于链表的一些重要操作(Important operations on a Linked List)中删除整个 ...

  7. OutOfMemoryError异常——Java堆溢出。

    https://blog.csdn.net/en_joker/article/details/79726975 (将堆的最小值-Xms参数与最大值-Xmx参数设置为一样即可避免堆自动扩展),通过参数- ...

  8. _CrtSetBreakAlloc简单内存泄漏检测方法,解决Detected memory leaks!问题

    我的环境是: XP SP2 . VS2003 最近在一个项目中,程序退出后都出现内存泄漏: Detected memory leaks! Dumping objects -> {98500} n ...

  9. valgrind检查代码内存泄漏,5种内存泄漏情况

    摘要: valgrind是linux下用于调试程序和查找内存泄露的常用工具.valgrind会报告5种内存泄露,"definitely lost", "indirectl ...

随机推荐

  1. python的类和对象——类的静态字段番外篇

    什么是静态字段 在开始之前,先上图,解释一下什么是类的静态字段(我有的时候会叫它类的静态变量,总之说的都是它.后面大多数情况可能会简称为类变量.): 我们看上面的例子,这里的money就是静态字段,首 ...

  2. Oracle的sql导入

    sqlldr: 一般用于导入以任何后缀结束的文件,我这次就是因为要导入一张以.20160101为后缀的文件,当初简直束手无策 结合input.ctl使用,可以在DOS下使用,可以对一张表导入数十万,百 ...

  3. MVC和MVVM对比

    被误解的 MVC MVC 的历史 MVC,全称是 Model View Controller,是模型 (model)-视图 (view)-控制器 (controller) 的缩写.它表示的是一种常见的 ...

  4. unity中鼠标左键控制摄像机视角上下左右移动

    enum RotationAxes { MouseXAndY, MouseX, MouseY } RotationAxes axes = RotationAxes.MouseXAndY; //@Hid ...

  5. python之路---模块

    在python中,为了简少代码重复量,我们将引用了函数,面向对象 等方法.为了能够让这些函数,面向对象可以再其他python文件中引用,我们将功能函数写在一个py文件中,该py文件即是一个模块,可以共 ...

  6. http/ftp等的URL匹配正则表达式 ZT

    网上流传着多种匹配URL的正则表达式版本,但我经过试验,最好用的还是从stackoverflow上查到的: (https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_| ...

  7. echarts背景分割区域填充不同颜色(x轴为time),实时刷新

    先来看下图片吧,这是实现效果: 思路: 因为要实时刷新,可以使用setInterval(),但是要控制好定时器的起与停,否则容易错乱以及页面卡死: 主要就是利用定时器五秒刷新,重绘echarts图:= ...

  8. HttpConnection详解【转】

    HttpConnection详解[转]   HttpURLConnection对象  1.从Internet获取网页,发送请求,将网页以流的形式读回来. 步骤:1)创建一个URL对象:URL url ...

  9. mysql oracle 数据库备份

    mysql 备份与还原 转载:https://blog.csdn.net/win_turn/article/details/60880990 备份数据库 数据库名叫dddd  mysqldump -u ...

  10. 初学java集合框架笔记

    List接口常用方法: 方法名 说    明 boolean add(Object o) 在列表的末尾顺序添加元素, 起始索引位置从0开始 void add(int index,Object o) 在 ...