本篇文章理解源自于《深入理解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. spring cloud(二)服务(注册)中心Eureka

    Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service Registry和Service Discovery实现.也是springcloud体系中最重要最核心的组 ...

  2. python编码及转换

    第一种:ASCII码 ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于 ...

  3. 第一个Spring 程序

    一 搭建好开发环境 JDK Eclipse 等 二 下载jar包 https://commons.apache.org/logging/ https://repo.spring.io/release/ ...

  4. ADO.NET之SqlConnection、sqlcommand的应用(学习笔记)

    一.知识描述点 1.SqlConnection (1)使用SqlConnection类可以连接到SQL Server数据库.SqlConnection对象的主要属性和方法如下: ——属性:Connec ...

  5. 解决jmeter乱码

    1. 解决响应数据乱码的方法:     在jemter文件下找到bin\jmeter.properties添加下面一句话:    sampleresult.default.encoding=UTF-8 ...

  6. os、os.path模块(文件/目录方法)

    1.模块的概念:模块是一个包含所有定义的变量.函数的文件,模块可以被其余模块调用. 2.利用OS模块实现对系统文件的. os模块中常见的方法: gercwd()     返回当前工作目录 chdir( ...

  7. 关于Idea启动配置tomcat

    1.打开file中setting中搜索Application Servers,如下图 2.添加服务器类型,例如tomcat,如下图,添加完成之后可以选定tomcat的目录,tomcat Home配置t ...

  8. nginx的变量参数 详解

    $args #请求中的参数值 $query_string #同 $args $arg_NAME #GET请求中NAME的值 $is_args #如果请求中有参数,值为"?",否则为 ...

  9. contos防爆力破解密码

    最近看了一篇文章ssh的爆力破解所以自己就做了一下防爆力破解denyhost 下载denyhost的软件包并上传的服务器下载地址https://sourceforge.net/projects/den ...

  10. s21day10 python笔记

    s21day10 python笔记 一.函数补充 1.1 参数 基本参数知识 def get_list_date(aaa): #aaa:形式参数(形参) 任意个数 v = [11,22,33,44] ...