JVM中的异常发生

  Java虚拟机规范中除了程序计数器外,其他几个运行时区域都有发生OutOfMemoryError异常的可能。
  本章笔记通过代码来验证Java虚拟机规范中描述的各个运行时区域存储的内容、以及在以后遇到实际的内存溢出异常时,能根据异常的信息快速判断是哪个区域出现的内存溢出、怎样的代码可能会导致这些区域的内存溢出、以及这些问题该如何处理。
  1. Java堆溢出:Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径避免垃圾回收机制清除对象,就会在对象数量到达最大堆的容量限制后产生内存溢出异常。-Xms设置堆最小值、-Xmx设置堆最大值、-XX:+HeapDumpOnOutOfMemoryError设置当虚拟机出现OOM异常时,dump出当前内存堆转存快照。其中当把堆最小值与最大值设置相同时候,堆为不可扩展。

    /**
    * -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
    */
    public class HeapOOM {
    static class OOMObject {
    Object obj = new Object();
    }
    public static void main(String[] args) {
    List<OOMObject> list = new ArrayList<OOMObject>();
    while( true) {
    list.add( new OOMObject());
    }
    }
    }
    要解决这个区域的异常,可以通过内存映像分析工具堆dump出来的快照进行分析,重点是确认内存是出现了泄漏(Memory Leak)还是内存溢出(Memory Overflow)。如果是内存泄漏通过工具查看相关引用链。如果是内存溢出,应当检查虚拟机的堆参数,与机器物理内存对比确认是否可以调大,代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况。
  2. 虚拟机栈和本地方法栈溢出:Java虚拟机中关于虚拟机栈和本地方法栈描述了两种异常
    StackOverflowError:线程请求的栈深度大于虚拟机所允许的最大深度时抛出
    OutOfMemoryError:虚拟机在扩展栈时无法申请到足够的内存空间时抛出
    -Xoss参数设置本地方法栈内存容量,-Xss参数设置虚拟机栈内存容量。
    /**
    * -Xss128k
    * 使用-Xss减小栈内存容量.
    * 定义大量本地变量,增加方法帧中本地变量表长度.
    */
    public class JavaVMStackSOF {
    private int stackLength = 1;
    public static void main(String[] args) {
    JavaVMStackSOF javaVMStackSOF = new JavaVMStackSOF();
    try {
    javaVMStackSOF.test();
    } catch(java.lang.StackOverflowError e) {
    System. out.println(javaVMStackSOF. stackLength);
    }
    }
    public void test() {
    ++ stackLength;
    test();
    }
    }
    单线程下,无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法分配时,虚拟机抛出的都是StackOverflowError异常。如果通过建立线程的方式可以产生内存溢出异常,但这样产生的异常与栈空间是否足够大并没有任何的关系,因为这种情况下,给线程的栈分配的内存越大,越容易产生内存溢出异常。原因:栈的生命周期与线程相同,每个线程分配到的栈容量越大,可以创建的线程数量就会越少。另外,虚拟机中剩余内存计算方法计算如下:剩余内存 = 操作系统限制进程内存 - 最大堆容量 - 最大方法区容量。程序计数器消耗内存很小,可忽略、若虚拟机进程本身不计算的话,剩下的就是虚拟机栈和本地方法栈分了。因此如果是建立过多线程导致的内存溢出,在不能减少线程数或者更换64位虚拟机的情况下,就只能通过减少最大堆(我认为应该也可以减少最大方法区)和减少栈容量来换取更多的线程了。
  3. 运行时常量池溢出:运行时常量分配在方法区内,可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制常量池的容量。实验代码在jdk 1.7.0_72上实验失败。
    /**
    * -XX:PermSize=10M -XX:MaxPermSize=10M
    * @author Administrator
    *
    */
    public class RuntimeConstantPoolOOM {
    public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    int i = 0;
    while( true) {
    list.add(String. valueOf(i++).intern());
    }
    }
    }
    运行时常量池溢出,在OutOfMemoryError后面跟随的提示信息是"PermGen space",说明运行时常量池属于方法区的一部分。
  4. 方法区溢出:方法区溢出是一种常见的内存溢出异常,可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,在经常动态生成大量Class的应用中,需要特别注意类的回收状况。动态生成Class可以使用CGLib字节码增强、动态JSP文件、基于OSGi的应用。下面的代码就是借助CGLib使方法区出现内存溢出异常
    /**
    * -XX:PermSize=10M -XX:MaxPermSize=10M
    */
    public class JavaMethodAreaOOM {
    public static void main(String[] args) {
    while( true) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(OOMObject. class);
    enhancer.setUseCache( false);
    enhancer.create();
    }
    }
    static class OOMObject {
    }
    }
  5. 本机直接内存溢出:直接内存(DirectMemory)容量可以通过-XX:MaxDirectMemorySize指定,如果不指定MaxDirectMemorySize默认值与Java堆的最大值一样。下面的代码使用Unsafe功能申请分配内存。
    /**
    * -Xmx20M -XX:MaxDirectMemorySize=10M
    */
    public class DirectMemoryOOM {
    private static final int _1MB = 1024*1024;
    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
    Field unsafeField = sun.misc.Unsafe.class.getDeclaredFields()[0];
    unsafeField.setAccessible( true);
    sun.misc.Unsafe unsafe = (sun.misc.Unsafe)unsafeField.get( null);
    while( true) {
    unsafe.allocateMemory( _1MB);
    }
    }
    }

方法入参建议使用JavaBean传递

  当方法参数很多时候,不便于阅读和维护,因此建议使用一个JavaBean来包装入参。这个建议的另外一方面因素请先考虑下面这个问题

public class MyBean {
private final double a;
private final double b; public MyBean(double a, double b) {
this.a = a;
this.b = b;
} public double getA() {
return a;
} public double getB() {
return b;
}
}
public class StackOverflowTestA {

    private static int invokeMethodCount = 0;

    public static void method(MyBean myBean) {
++invokeMethodCount;
System.out.println(invokeMethodCount);
//method(new MyBean(myBean.getA(), myBean.getB()));
method(myBean);
} public static void main(String[] args) {
try {
method(new MyBean(1L, 2L));
} catch(Throwable e) {
System.out.println("调用次数 : " + invokeMethodCount);
}
} }
public class StackOverflowTestB {

    private static int invokeMethodCount = 0;

    public static void method(long a, long b) {
++invokeMethodCount;
System.out.println(invokeMethodCount);
method(a, b);
} public static void main(String[] args) {
try {
method(1L, 2L);
} catch(Throwable e) {
System.out.println("调用次数 : " + invokeMethodCount);
}
}
}

  我们都知道Java虚拟机栈的空间有限,当出现无限递归的代码时就会发生StackOverflowError错误。
  那么请思考上面代码中StackOverflowTestA和StackOverflowTestB哪一个递归的深度更深一些?如果StackOverflowTestB中method方法入参变为两个short类型呢?

了解OutOfMemoryError异常 - 深入Java虚拟机读后总结的更多相关文章

  1. 深入理解java虚拟机读后总结

    之前看过,很多会遗忘,标记一下,温故知新.(明天的我一定会感谢现在努力的自己. ) 一.运行时数据区域 Java虚拟机管理的内存包括几个运行时数据内存:方法区.虚拟机栈.本地方法栈.堆.程序计数器,其 ...

  2. Java内存区域 - 深入Java虚拟机读后总结

    Java虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域有各自的用途,有各自的创建时间和销毁时间,有的区域随着虚拟机进程的启动而存在,有的区域则是依赖用户线程的启动 ...

  3. 深入理解java虚拟机读后总结(个人总结记录)

    1.jvm布局:   jdk1.6版本JVM布局分为:heap(堆),method(方法区),stack(虚拟机栈),native stack(本地方法栈),程序计数器共五大区域. 其中方法区包含运行 ...

  4. 【书海】《Head First Java》 ——读后总结

    <Head First Java> 中文版 (第二版) IT`huhui前言录 <Head First Java>这本书我不算特别细的看了一遍.认为十分适合初学者,甚至是没接触 ...

  5. 《Effective Java》——读后总结

    这本书在Java开发的行业里,颇有名气.今天总算是粗略的看完了…后面线程部分和序列化部分由于心浮气躁看的不仔细.这个月还剩下一周,慢慢总结消化. 1.静态工厂方法代替构造器 静态工厂方法有名称,能确切 ...

  6. 《像计算机科学家一样思考Java》—— 读后总结

    本书属于入门级的Java书籍,与其他的向编程思想.核心技术不同的是,这本书不是按部就班的讲解java变成知识,而是随着语言的深入慢慢增加知识点. 这本书以一个语言开发者的角度,深入浅出的讲解了Java ...

  7. java 核心技术 读后总结

    总结 1.少用八进制,以及二进制. 那么就是直接用16进制或10进制吗?额,想当年有这样搞过,后面就uuid了. 2.>>>用0填充高位>>用符号位填充高位<< ...

  8. 《Effective Java》—— 读后总结

    这本书在Java开发的行业里,颇有名气.今天总算是粗略的看完了...后面线程部分和序列化部分由于心浮气躁看的不仔细.这个月还剩下一周,慢慢总结消化.

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

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

随机推荐

  1. jQuery中Ajax的应用

    一.Ajax介绍 1.什么是Ajax 异步的JavaScript与XML技术,是一种广泛应用在浏览器的网页开发技术. 2.Ajax的优点 a.不需要任何浏览器插件,在任何支持JavaScript的浏览 ...

  2. 代码高亮插件推荐——SyntaxHighlighter++

    SyntaxHighlighter++这个插件的最大的优点就是可以在编辑器的下方有一个输入框,里面可以输入代码,然后插入到文章中.就不用编辑文章的时候,在可视化和文本之间来回切换了.非常适合不熟悉ht ...

  3. macvim打造python IDE

    昨天安装了macvim,今天在上面配置了一下python的ide: 大家也可参考http://blog.dispatched.ch/2009/05/24/vim-as-python-ide/ 1.文法 ...

  4. C++ 动态库导出函数名“乱码”及解决

    C++ 动态库导出函数名“乱码”及解决 刚接触C++,在尝试从 dll 中导出函数时,发现导出的函数名都“乱码”了. 导出过程如下: 新建一个Win32项目: 新建的解决方案里有几个导出的示例: // ...

  5. C#:iterator 迭代器/partial class 分布类/泛型

    C#:iterator 迭代器/partial class 分布类/泛型 iterator 迭代器 写个最简单的迭代,(迭代一个字符串数组): 1.实现接口中的方法: 1 using System; ...

  6. uploadify的使用

    uploadify的使用 课程设计需要实现上传文件模块,本来ASP.NET是有内置的控件,但是ASP.NET MVC没有,所以就有两种方法:自定义和采用第三方插件.由于时间的关系,故采用第三方插件:u ...

  7. 字符转十六进制 String => HEX using "hexdump" on linux

    hexdump 是一款非常简单的有效的将别的形式的文档转换成十六进制的工具. 最详细的使用说明都在 man hexdump 里面,请自行查阅. 这里我摘出几个常用的例子,一目了然: 这里我做了一个实验 ...

  8. U盘安装CentOS 6.4 + Windows 7双系统 (Windows 7下安装 CentOS 6.4)

    最近在看<鸟哥私房菜:基础学习篇>,觉得很不错,想要装个windows 7 和 CentOS 6.4 双系统,在网上找了很多教程,觉得乱七八糟的,弄得很复杂,而且很多都不是很完整,对于新手 ...

  9. 安卓开发16:Spinner 下拉列表控件

    Spinner 下拉列表控件 创建一个activity_main.xml文件: <RelativeLayout xmlns:android="http://schemas.androi ...

  10. js区分汉字和字符,校验长度

    遇到这么一个问题,    长度限制输入150个英文字符(小于等于150个英文字符长度),超出则直接禁止输入,并提醒:摘要输入必须小于等于75个中文字符长度! 长度校验倒是没问题,但是要区分汉字还是英文 ...