一、Java堆溢出

测试代码:

/**
* <p>Java堆异常测试</p>
* <code>VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=E:\job</code>
* <p>以上参数的含义是:限制Java堆大小为20MB,不可扩展</p>
* <p>通过此参数可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照</p>
* <p>将内存溢出快照存储到指定路径:E:\job</p>
*/
public class HeapOOM { static class OOMObject {} public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while(true) {
list.add(new OOMObject());
}
}
}

执行结果:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to E:\job ...
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2245)
at java.util.Arrays.copyOf(Arrays.java:2219)
at java.util.ArrayList.grow(ArrayList.java:242)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
at java.util.ArrayList.add(ArrayList.java:440)
at com.sandy.jvm.chapter02.HeapOOM.main(HeapOOM.java:19)

针对这类异常,可通过分析工具(如Eclipse Memory Analyzer)对异常快照进行分析,找到具体发生异常代码。

二、虚拟机栈和本地方法栈的溢出

由于HotSpot虚拟机不区分虚拟机栈和本地方法栈,因此不需要设置-Xoss参数,栈容量只由-Xss参数设定。

针对栈,虚拟机规范了两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

测试代码:

/**
* VM Args: -Xss128k
*/
public class JavaStackSOF { private int stackLength = 1; public void stackLeak() {
stackLength++;
stackLeak();
} public static void main(String[] args) {
JavaStackSOF oom = new JavaStackSOF();
try{
oom.stackLeak();
}catch(Throwable e) {
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}

执行结果:

stack length:2101
Exception in thread "main" java.lang.StackOverflowError
at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:13)
at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)
at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)
at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)
at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)
at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)
at com.sandy.jvm.chapter02.JavaStackSOF.stackLeak(JavaStackSOF.java:14)
...

单个线程下,无论是由于栈帧太大还是虚拟机栈容量太小,虚拟机抛出的都是StackOverflowError。

  • 如果虚拟机扩展栈时,无法申请到足够的空间,将抛出OutOfMemoryError异常。

测试代码:

/**
* VM Args: -Xss2M (这时候不妨设置大一点点)
*/
public class JavaStackOOM {
private void dontstop() {
while(true) { }
} public void stackLeakThread() {
while(true) {
new Thread() {
@Override
public void run() {
dontstop();
}
}.start();
}
} public static void main(String[] args) {
JavaStackOOM oom = new JavaStackOOM();
oom.stackLeakThread();
}
}

测试结果会抛出:Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

这种情况下,为每个线程栈分配的内存越大,越容易出现内存溢出。

原因是:操作系统为每个进程分配的内存有限制,比如32位windows限制为2GB,栈内存=2GB-Xmx(最大堆容量)-MaxPermSize(最大方法区容量),每个线程分配的栈内存越大,可创建的线程数就越少,越容易把剩下内存耗尽。

三、方法区和常量池溢出

String.intern()是一个Native方法,作用是:如果字符串常量池已包含了一个等于String对象的字符串,则返回这个字符串的String对象,否则,将此String对象包含的字符串添加到常量池,并且返回此String对象的引用。

在jdk1.6及之前版本,常量池分配在永久代中,可通过-XX:PermSize和-XX:MaxPermSize限制方法区大小。

/*
* VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M(限制常量池容量)
*
*/
public class RunTimeConstantPoolOutOfMemoryError { public static void main(String[] args) {
// 使用list保持常量池引用,避免常量池内的数据被垃圾回收清除
List<String> list = new ArrayList<>();
long i = 0;
while (true) {
String string = (i++) + "";
list.add(string.intern());
}
}
}

此段代码在jdk6之前的版本中运行时会产生:Exception in thread "main" java.lang.OutOfMemoryError: PermGen space 其中PermGen space指示内存溢出发生在运行时常量池中。

在jdk7的环境中运行得到的结果却是: Exception in thread "main" java.lang.OutOfMemoryError: Java heap space  指示内存溢出发生在堆中而不是方法区中的常量池。

因为在 JDK1.2 ~ JDK6 的实现中,HotSpot 使用永久代实现方法区,而从 JDK7 开始 Oracle HotSpot 开始移除永久代,JDK7中符号表被移动到 Native Heap中,字符串常量和类引用被移动到 Java Heap中。在 JDK8 中,永久代已完全被元空间(Meatspace)所取代。

四、本机直接内存溢出

DirectMemory容量可通过-XX:MaxDirectMemorySize指定,如果不指定,默认与Java堆最大值一样。

测试代码:

/**
* VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M
*/
public class DirectMemoryOOM { private static final int _1MB = 1024*1024; public static void main(String[] args) throws Exception {
Field unsafeFiled = Unsafe.class.getDeclaredFields()[0];
unsafeFiled.setAccessible(true);
Unsafe unsafe = (Unsafe)unsafeFiled.get(null);
while(true) {
unsafe.allocateMemory(_1MB);
}
}
}

测试结果:

Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at com.sandy.jvm.chapter02.DirectMemoryOOM.main(DirectMemoryOOM.java:22)

由DirectMemory导致的内存溢出,明显特征是在Heap Dump文件中不会看见明显的异常,如果发现Dump文件很小,而程序直接或间接使用了NIO,那可以考虑是否由此原因引起。

《深入理解java虚拟机》笔记(3)实战:OutOfMemoryError异常的更多相关文章

  1. Java内存区域与内存溢出异常——深入理解Java虚拟机 笔记一

    Java内存区域 对比与C和C++,Java程序员不需要时时刻刻在意对象的创建和删除过程造成的内存溢出.内存泄露等问题,Java虚拟机很好地帮助我们解决了内存管理的问题,但深入理解Java内存区域,有 ...

  2. Java虚拟机学习总结之OutOfMemoryError异常

    参考:深入理解java虚拟机一书 开始之前,我们也应当搞清楚连个概念,内存泄漏Memory Leak 内存溢出: 内存泄漏:程序中间动态分配了内存,但是在程序结束时没有释放内存,造成这部分内存不可用. ...

  3. 深入理解Java虚拟机笔记

    1. Java虚拟机所管理的内存 2. 对象创建过程 3. GC收集 4. HotSpot算法的实现 5. 垃圾收集器 6. 对象分配内存与回收细节 7. 类文件结构 8. 虚拟机类加载机制 9.类加 ...

  4. 深入理解java虚拟机笔记Chapter12

    (本节笔记的线程收录在线程/并发相关的笔记中,未在此处提及) Java内存模型 Java 内存模型主要由以下三部分构成:1 个主内存.n 个线程.n 个工作内存(与线程一一对应) 主内存与工作内存 J ...

  5. 深入理解Java虚拟机:OutOfMemory实战

    在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(下文称OOM)异常的可能,本节将通过若干实例来验证异常发生的场景.并且会初步介绍 ...

  6. 深入理解java虚拟机:笔记

    1.运行时数据区域 1.程序计数器 当前线程执行字节码的行号指示器,字节码解释器工作通过改变这个计数器的值来选取下一条需要执行的字节码指令,每一个线程拥有独立的程序计数器,线程私有的内存 2.虚拟机栈 ...

  7. 垃圾收集器与内存分配策略——深入理解Java虚拟机 笔记二

    在本篇中,作者大量篇幅介绍了当时较为流行的垃圾回收器,但现在Java 14都发布了,垃圾收集器也是有了很大的进步和发展,因此在此就不再对垃圾收集器进行详细的研究.但其基本的算法思想还是值得我们参考学习 ...

  8. 深入理解java虚拟机笔记Chapter7

    虚拟机类的加载机制 概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类的加载机制. 类加载的时机 J ...

  9. 深入理解java虚拟机笔记Chapter2

    java虚拟机运行时数据区 首先获取一个直观的认识: 程序计数器 线程私有.各条线程之间计数器互不影响,独立存储. 当前线程所执行的字节码行号指示器.字节码解释器工作时通过改变这个计数器值选取下一条需 ...

  10. 深入理解java虚拟机笔记之一

    Java的技术体系主要有支撑java程序运行的虚拟机,提供各开发领域接口支持Java API,java编程语言及许多第三方java框架( 如Spring,Structs等)构成. 可以把Java程序设 ...

随机推荐

  1. 文件操作:os模块与os.path模块

    一.os与os.path 原创:http://www.cnblogs.com/lovemo1314/archive/2010/11/08/1871781.html os模块用于处理文件及文件夹,包括文 ...

  2. STL中mem_fun和mem_fun_ref的用法

    例如:假设有如下的代码: class Employee { public: int DoSomething(){} } std::vector<Employee> Emps; 假设我们要调 ...

  3. POJ3107Godfather(求树的重心裸题)

    Last years Chicago was full of gangster fights and strange murders. The chief of the police got real ...

  4. k8s-部署WEB-UI(dashboard)

    [root@k8s-master dashboard]# pwd/usr/local/src/kubernetes/cluster/addons/dashboard [root@k8s-master ...

  5. java-02 for循环,while循环

    ###############获取一个数字的每位数字############### #############1-100奇数和偶数和############## public class Sum { ...

  6. MySQL条件判断处理函数_20160925

    MySQL条件判断处理 一.假如我想把salesperson 分成 5组,计算每个销售分组的业绩 首先先将销售分组 SELECT *, CASE WHEN salesperson IN (" ...

  7. bzoj 4515: 游戏 树链剖分+线段树

    题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=4515 题解: 先让我%一发lych大佬点我去看dalao的题解 讲的很详细. 这里纠正一 ...

  8. C#中使用GetCursorPos获取屏幕坐标

    [StructLayout(LayoutKind.Sequential)] public struct POINT { public int X; public int Y; public POINT ...

  9. cocos2d-x CCHttpRequest获取网络图片并显示

    转自:http://www.cnblogs.com/hzj730/p/3178431.html //图片结构 class imgstruct : public CCObject { public: i ...

  10. 南阳oj水题集合,语言的灵活运用

    a+b 输入 输入两个数,a,b 输出 输出a+b的值 样例输入 2 3 样例输出 5 c/c++ #include<iostream> using namespace std; int ...