《深入理解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程序设 ...
随机推荐
- deep QA 基于生成的chatbot系统
deep QA: https://github.com/Conchylicultor/DeepQA 基于论文:https://arxiv.org/pdf/1506.05869.pdf 基于生成的c ...
- MFC模态对话框程序不响应OnIdle
从代码分析原因吧: OnIdle函数在MFC的CWinThread::Run函数中被调用,如下 // main running routine until thread exits int CWinT ...
- PHP 正则表达示
PHP 正则表达示 php如何使用正则表达式 正则表达式基本元字符 #正则表达式示例 ^:匹配输入字符串开始的位置.如果设置了 RegExp 对象的 Multiline 属性,^ 还会与“\n”或“\ ...
- 《TCP/IP详解卷一:协议》数据链路层(一)
版权声明:本文为博主原创文章,转载请标注转载链接,谢谢. 目录(?)[+] 引言 在TCP/IP协议族中,链路层主要有三个目的: 为IP模块发送和接收IP数据报. 为ARP模块 ...
- mina2中的线程池
一.Mina中的线程池模型 前面介绍了Mina总体的层次结构,那么在Mina里面是怎么使用Java NIO和进行线程调度的呢?这是提高IO处理性能的关键所在.Mina的线程调度原理主要如下图所示: A ...
- 10 Vue 学习 shortList页面
1: shortList页面代码如下: <template> <div class="fillcontain"> <head-top></ ...
- win7 64位搭建Mantis 缺陷管理系统
什么是Mantis MantisBT is a free popular web-based bugtracking system (feature list). It is written in t ...
- In-App Purchase Configuration Guide for iTunes Connect---(一)----Introduction
Introduction In-App Purchase is an Apple technology that allows your users to purchase content and s ...
- hdu 4123 Bob’s Race (dfs树上最远距离+RMQ)
C - Bob’s Race Time Limit:2000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Subm ...
- 使用gdb调试c程序莫名退出定位 exit 函数
gdb 程序名称 b exit //设置exit函数断点 run //运行程序 bt //查看程序调用堆栈,定位到exit所在行