Java内存溢出异常(上)
上一篇文章我们讲了JVM运行时数据区域与内存溢出异常,其中对于内存溢出异常这部分将的不够详细,这篇文章将着重讲解Java内存溢出异常的相关知识。如果有没看过上一篇文章的小伙伴们,请点击Java内存区域与内存溢出异常。
Java的内存溢出异常主要分为两类:分别是内存溢出和栈溢出。在以下几种情况,会抛出内存异常:Java堆溢出、虚拟机栈和本地方法栈溢出、方法区和运行时常量池溢出、以及本机直接内存溢出,下面讲一一介绍这几类异常。
Java堆溢出
在Java内存区域与内存溢出异常中讲过,Java堆主要是用来存储对象实例的。这部分的内存区域的大小可以通过-Xms参数和-Xmx参数进行设置,通常将-Xms和-Xmx的值设置为相同的值,以减少内存扩展或者收缩时的开销。
Java堆的空间是有限的,受到物理内存与虚拟机内存的双重限制(通常虚拟机内存的会设置成小于物理内存)。因此,如果对象实例的数量不断增加,而垃圾回收机制没有进行及时清理的时候,对象实例所占用的空间就会达到Java堆的空间最大值。此时,就会因为Java堆内存不足,导致无法为新的实例分配空间,从而抛出OutOfMemoryError异常。
通过设置-Xms20m -Xmx20m运行以下代码可以模拟这一情况:
/**
* VM Args: -Xms20m -Xmx20m
*
* @author bdq
*/
public class HeapOOM {
static class OOMObject { } public static void main(String[] args) {
List<OOMObject> objects = new ArrayList<>();
while (true) {
objects.add(new OOMObject());
}
}
}
运行结果:
java.lang.OutOfMemoryError: Java heap space
这即是常见的OOM异常,针对这类异常,往往在打印异常信息的同时会进一步提示异常原因,如上图所示的”Java heap space”。当然,只靠这点信息不足以判断到底是内存容量设置小了,还是出现了内存泄漏(关于内存泄漏的知识将会在后面的文章中进行讲述)。因此我们还要辅以其他手段来进一步确定问题的根源,比如加上-XX:+HeapDumpOnOutOfMemoryError参数使得虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照,然后用相关的工具进行分析。这类知识,本篇文章暂不作过多的讲解,将会在后面的文章一一介绍。
虚拟机栈和本地方法栈溢出
为什么要把虚拟机栈和本地方法栈的溢出放在一起讨论呢,因为在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈。对于HotSpot来说,虽然说-Xoss参数是用来设置本地方法栈大小,但实际上是无效的,栈的容量只由-Xss参数设定。
在Java虚拟机规范中对虚拟机栈和本地方法栈描述了两种异常:
- 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
- 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
这种分类其实并不是很明确,因为内存太小或者已使用的栈空间太大都会导致栈空间无法继续分配。
StackOverflowError的出现条件很简单,下面这段简单的代码就会出现栈溢出:
public class JavaVMStackSOF {
private int stackLength = 1; public void stackLeak() {
stackLength++;
stackLeak();
} public static void main(String[] args) {
JavaVMStackSOF javaVMStackSOF = new JavaVMStackSOF();
try {
javaVMStackSOF.stackLeak();
} catch (Throwable e) {
System.out.println("Stack length:" + javaVMStackSOF.stackLength);
throw e;
}
}
}
运行结果如下:
Stack length:18663
Exception in thread "main" java.lang.StackOverflowError
at cn.bdqfork.jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at cn.bdqfork.jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at cn.bdqfork.jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
at cn.bdqfork.jvm.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)
......
对上面的运行结果,不同的计算机Stack length的大小是不确定的,从输出的异常信息来看,是因为stackLeak方法递归调用层数过多导致的。在大多数情况下,栈深度在虚拟机默认参数下是够用的。
OutOfMemoryError异常比较难以出现,一般发生在多线程环境下。当创建一个线程时,虚拟机会分配一个私有的栈空间给相应的线程,这个空间的大小可以用-Xss参数来设置。通过不断的创建新的进程,可以产生内存溢出异常。
原因是这样的,当进程运行时,操作系统分配给进程的内存是有限的,Java堆和方法区这两部分占了大部分,忽略到程序计数器所占用的很小的一块内存,不计算虚拟机本身占用的内存,剩下的就由虚拟机栈和本地方法栈所占用。因此,创建的线程数量到达一定程度时,虚拟机栈和本地方法栈所占用的空间就会使得进程的内存空间不够用,从而抛出内存溢出异常。
这部分的测试代码如下:
public class JavaVMStackOOM {
private void dontStop() {
while (true) { }
} public void stackLeakByThread() {
while (true) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
dontStop();
}
});
thread.start();
}
} public static void main(String[] args) {
JavaVMStackOOM javaVMStackOOM = new JavaVMStackOOM();
javaVMStackOOM.stackLeakByThread();
}
}
这段代码的运行有一定的风险,因为Java的线程并不是完全的用户级线程,有映射到操作系统的部分,所以可能会产生系统假死的现象,请谨慎运行。
运行结果如下:
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
由此可以看出,我们在进行多线程开发时,对于线程的数量要有一定的把握,线程池的复用是很有必要的。
受限于篇幅原因,剩余的知识点,将会在下一篇进行讲解。
Java内存溢出异常(上)的更多相关文章
- Java内存溢出异常(下)
此篇是上一篇文章Java内存溢出异常(上)的续篇,没有看过的同学,可以先看一下上篇.本篇文章将介绍剩余的两个溢出异常:方法区和运行时常量池溢出. 方法区和运行时常量池溢出 这部分为什么会放在一起呢?在 ...
- JVM(2) Java内存溢出异常
在Java虚拟机运行时数据区中,除了程序计数器之外,虚拟机栈.本地方法栈.方法区和Java堆都有发生OutOfMemoryError(简称OOM)异常的可能. 一.Java堆溢出 Java堆用于存储对 ...
- java内存溢出异常
名称 特征 作用 配置参数 异常 程序 计数器 占用内存小,线程私有, 生命周期与线程相同 大致为字节码行号指示器 无 无 虚拟机栈 线程私有,生命周期与线程 相同,使用连续的内存空间 Java 方法 ...
- 模拟Java内存溢出
本文通过修改虚拟机启动参数,来剖析常见的java内存溢出异常(基于jdk1.8). 修改虚拟机启动参数Java堆溢出虚拟机栈溢出方法区溢出本机直接内存溢出 修改虚拟机启动参数 这里我们使用的是ID ...
- 如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码
程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...
- 深入理解java虚拟机系列(一):java内存区域与内存溢出异常
文章主要是阅读<深入理解java虚拟机:JVM高级特性与最佳实践>第二章:Java内存区域与内存溢出异常 的一些笔记以及概括. 好了開始.假设有什么错误或者遗漏,欢迎指出. 一.概述 先上 ...
- JVM自动内存管理-Java内存区域与内存溢出异常
摘要: JVM内存的划分,导致内存溢出异常的可能区域. 1. JVM运行时内存区域 JVM在执行Java程序的过程中会把它所管理的内存划分为以下几个区域: 1.1 程序计数器 程序计数器是一块较小的内 ...
- Java虚拟机内存溢出异常--《深入理解Java虚拟机》学习笔记及个人理解(三)
Java虚拟机内存溢出异常--<深入理解Java虚拟机>学习笔记及个人理解(三) 书上P39 1. 堆内存溢出 不断地创建对象, 而且保证创建的这些对象不会被回收即可(让GC Root可达 ...
- 《深入理解Java虚拟机》-----第2章 Java内存区域与内存溢出异常
2.1 概述 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任 ...
随机推荐
- C++ 动态链接库 DLL 的一些笔记
DLL 文件源代码: // test.h #ifdef TEST_EXPORTS #define TEST_API __declspec(dllexport) #endif class TEST_AP ...
- pgsql事务与并发控制
事务与并发控制 事务的基本的概念和性质 ACID: 原子性:一个事务要么全部执行,要么全部不执行 一致性:执行事务的时候,数据库从一个一致的状态变更到另一个状态 隔离性: 确保在并发执行的时候,每个事 ...
- CentOS7安装jdk8及环境变量配置
下载jdk8 这里可以使用Windows下载,然后传到虚拟机 进入jdk下载页面 https://www.oracle.com/technetwork/java/javase/downloads/in ...
- Django 中使用kindeditor
KindEditor 是一套开源的在线HTML编辑器,主要用于让用户在网站上获得所见即所得编辑效果,开发人员可以用 KindEditor 把传统的多行文本输入框(textarea)替换为可视化的富文本 ...
- python中assert()函数的使用
关于assert()函数的使用,主要参考博客https://blog.csdn.net/qq_37119902/article/details/79637578 assert函数主要是用来声明某个函数 ...
- python实现加密
1.md5加密 hashlib 库中包括如SHA1.SHA224.SHA256.SHA384.SHA512和MD5算法等 >>> import hashlib>>> ...
- Python的虚拟环境
Python自带env # 新建虚拟环境 python -m venv env_name # 激活虚拟环境 cd env_name cd Scripts activate # 退出虚拟环境 # 到达虚 ...
- Eclipse之JSP页面的使用
Eclipse之JSP页面的使用 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.使用Eclipse创建JSP文件 1>.点击new file,选择jsp File 2&g ...
- DirectX11 With Windows SDK--11 混合状态与光栅化状态
前言 虽然这一部分的内容主要偏向于混合(Blending),但这里还需提及一下,关于渲染管线可以绑定的状态主要有如下四种: 光栅化状态(光栅化阶段) 采样器状态(像素着色阶段) 混合状态(输出合并阶段 ...
- 第十二节: EF的三种模式(二) 之 ModelFirst模式(SQLServer为例)
一. 简介 顾名思义,ModelFirst是模型优先,是DBFirst的逆向模式,先建立实体数据模型,然后根据实体数据模型来生成数据库,从而驱动整个开发流程.(生成一个空的edmx文件,手动在里面建 ...