《深入理解java虚拟机》笔记(3)实战:OutOfMemoryError异常
一、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异常的更多相关文章
- Java内存区域与内存溢出异常——深入理解Java虚拟机 笔记一
Java内存区域 对比与C和C++,Java程序员不需要时时刻刻在意对象的创建和删除过程造成的内存溢出.内存泄露等问题,Java虚拟机很好地帮助我们解决了内存管理的问题,但深入理解Java内存区域,有 ...
- Java虚拟机学习总结之OutOfMemoryError异常
参考:深入理解java虚拟机一书 开始之前,我们也应当搞清楚连个概念,内存泄漏Memory Leak 内存溢出: 内存泄漏:程序中间动态分配了内存,但是在程序结束时没有释放内存,造成这部分内存不可用. ...
- 深入理解Java虚拟机笔记
1. Java虚拟机所管理的内存 2. 对象创建过程 3. GC收集 4. HotSpot算法的实现 5. 垃圾收集器 6. 对象分配内存与回收细节 7. 类文件结构 8. 虚拟机类加载机制 9.类加 ...
- 深入理解java虚拟机笔记Chapter12
(本节笔记的线程收录在线程/并发相关的笔记中,未在此处提及) Java内存模型 Java 内存模型主要由以下三部分构成:1 个主内存.n 个线程.n 个工作内存(与线程一一对应) 主内存与工作内存 J ...
- 深入理解Java虚拟机:OutOfMemory实战
在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(下文称OOM)异常的可能,本节将通过若干实例来验证异常发生的场景.并且会初步介绍 ...
- 深入理解java虚拟机:笔记
1.运行时数据区域 1.程序计数器 当前线程执行字节码的行号指示器,字节码解释器工作通过改变这个计数器的值来选取下一条需要执行的字节码指令,每一个线程拥有独立的程序计数器,线程私有的内存 2.虚拟机栈 ...
- 垃圾收集器与内存分配策略——深入理解Java虚拟机 笔记二
在本篇中,作者大量篇幅介绍了当时较为流行的垃圾回收器,但现在Java 14都发布了,垃圾收集器也是有了很大的进步和发展,因此在此就不再对垃圾收集器进行详细的研究.但其基本的算法思想还是值得我们参考学习 ...
- 深入理解java虚拟机笔记Chapter7
虚拟机类的加载机制 概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类的加载机制. 类加载的时机 J ...
- 深入理解java虚拟机笔记Chapter2
java虚拟机运行时数据区 首先获取一个直观的认识: 程序计数器 线程私有.各条线程之间计数器互不影响,独立存储. 当前线程所执行的字节码行号指示器.字节码解释器工作时通过改变这个计数器值选取下一条需 ...
- 深入理解java虚拟机笔记之一
Java的技术体系主要有支撑java程序运行的虚拟机,提供各开发领域接口支持Java API,java编程语言及许多第三方java框架( 如Spring,Structs等)构成. 可以把Java程序设 ...
随机推荐
- 数据可视化入门之show me the numbers
数据的可视化一直是自己瞎玩着学,近来想系统的学数据可视化的东西,于是搜索资料时看到有人推荐<show me the numbers>作为入门. 由于搜不到具体的书籍内容,只能 ...
- codewar代码练习1——8级晋升7级
最近发现一个不错的代码练习网站codewar(http://www.codewars.com).注册了一个账号,花了几天的茶余饭后时间做题,把等级从8级升到了7级.本文的目的主要介绍使用感受及相应题目 ...
- Java之动态代理简介
图截于<大话设计模式> Proxy模式是常用的设计模式,其特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 用户可以更 ...
- MySQL_各城市在线产品天订单数据20161130
#sealreport010 `tb010_02d`各城市在线产品天订单数据 #sealreport010 `tb010_02d`各城市在线产品天订单数据 SELECT d.ID,a.城市,a.在线日 ...
- ASM认证与口令文件
ASM认证 ORACLE ASM 实例没有数据字典,所以连接ASM 实例只能通过如下三种系统权限来进行连接: SYSASM,SYSDBA,SYSOPER. 可以通过如下三种模式来连接ASM 实例:1. ...
- poj 3415 Common Substrings —— 后缀数组+单调栈
题目:http://poj.org/problem?id=3415 先用后缀数组处理出 ht[i]: 用单调栈维护当前位置 ht[i] 对之前的 ht[j] 取 min 的结果,也就是当前的后缀与之前 ...
- Poco 编译mysql
POCO mysql需要自己添加connecter的header和lib MySQL Client: For the MySQL connector, the MySQL client librari ...
- pig ERROR 2997: Encountered IOException. File or directory null does not exist.
grunt> ls 2014-03-30 19:58:31,344 [main] ERROR org.apache.pig.tools.grunt.Grunt - ERROR 2997: Enc ...
- 《Java多线程编程核心技术》读后感(十五)
线程的状态 线程对象在不同的运行时期有不同的状态,状态信息就存在与State枚举类中. 验证New,Runnable,Terminated new:线程实例化后还从未执行start()方法时的状态 r ...
- openstack介绍(二)
OpenStack services 本节将详细描述OpenStack服务. Compute service overview(计算服务概述) 使用OpenStack云计算计算主机和管理系统.Open ...