本篇文章理解源自于《深入理解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 CoolBook』C扩展库_其六_线程

    GIL操作 想让C扩展代码和Python解释器中的其他进程一起正确的执行, 那么你就需要去释放并重新获取全局解释器锁(GIL). 在Python接口封装中去释放并重新获取全局解释器锁(GIL),此时本 ...

  2. MySQL造数据脚本-亲试

    DELIMITER $$CREATE DEFINER=`root`@`192.168.2.254` PROCEDURE `pjzzspdz_fpmx_initdata12101245`()BEGIN ...

  3. 2015-10-12 jQuery4

    十. 直接获取.编辑内容 1.获取内容 alert($("#d1").text());    //获取文本内容 alert($("#d1").html()); ...

  4. numpy delete

    手动安装 sudo rm -rf /usr/local/lib/python2.7/site-packages/numpy/ sudo rm -rf /usr/local/lib/python2.7/ ...

  5. LINQ分组取出第一条数据

    Person1: Id=, Name="Test1" Person2: Id=, Name="Test1" Person3: Id=, Name="T ...

  6. day057 基于对象和基于双下划线的多表查询

    单表查询和多表查询的添加与查询                   上述关系:author-authordetail是一对一的关系(外键在author上名为authors),  book-publis ...

  7. Linux Shell基础(下)

    Linux Shell基础(下) 目录 一.shell特殊符号cut命令 二.cut.sort.wc.uniq命令 三.tee.tr.split命令 四.简易审计系统 五.fork, exec, so ...

  8. python自学第13天 hashlib,re模块

    import hashlib sha=hashlib.sha3_512()#定义加密成什么格式 sha.update('how to use sha1 in 年后 '.encode('utf-8')) ...

  9. 素数定理π(n)~n/lnn弱化版证明

    在大半年前写的Miller-Rabin素数测试正确性证明中使用过此结论~当时完全不会证,现在进步了一点点会证弱化版的了Orz 完整版的素数定理: π(n):=|{p|p<=n,p是素数}| li ...

  10. 【linux基础】cuDNN版本查询

    参考 1. 查看cudnn版本; 完