如何构造jvm的堆溢出和栈溢出
构造堆溢出和栈溢出
Java虚拟机中描述了两种异常:
- 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常;—-栈溢出
- 如果在虚拟机中无法申请到足够多的内存空间,将抛出OutOfMemoryError异常。—-堆溢出
堆溢出
在java堆中只会产生OutOfMemoryError异常
首先,我们知道Java堆内存存放的是对象实例。所以原理上只要我们不断创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清楚这些对象,也就是说当Eden区满的时候,GC被触发时,让GC误以为内存中的对象还存活着,那么在对象数量达到最大堆容量限制的时候就会产生内存溢出的异常。
public class 堆溢出 {
static class OOMError{}
public static void main(String[] args) {
List<OOMError> list = new ArrayList<OOMError>();
while (true) {
list.add(new OOMError());
}
}
}
虽然这里产生了堆溢出,但是我们需要注意产生这个异常的原因是内存溢出还是内存泄露
首先我们要分清楚产生OutOfMemoryError异常的原因是内存泄露还是内存溢出,如果内存中的对象确实都必须存活着而不像上面那样不断地创建对象实例却不使用该对象,则是内存溢出,而像上面代码中的情况则是内存泄露。
如果是内存泄露,我们可以通过一些内存查看工具来查看泄露对象到GC Roots的引用链,找到泄露对象是通过怎样的路径与GC Roots相关联并导致GC无法自动回收这些泄露对象,掌握了这些信息,我们就能比较准确地定位出泄露代码的位置。
如果不是内存泄露,也就是说内存中的对象确实都还必须存活,那么应该检查虚拟机的堆参数,看看是否还可以将机器物理内存调大,同时在代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况。
栈溢出
虚拟机栈用于存储局部变量表、操作数栈、常量池引用等信息。
所以想让栈溢出,我们只需要定义大量的局部变量,增大此方法帧中本地变量表的长度或者设置-Xss参数减少栈内存容量,又或者无限递归调用方法产生新的栈帧都会产生StackOverflowError异常
public class 栈溢出 {
private int stackLength = 1;
public void addStackLength(){
stackLength++;
addStackLength();
}
public static void main(String[] args) throws Throwable{
栈溢出 oom = new 栈溢出();
try {
oom.addStackLength();
} catch (Throwable e) {
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}
·如果在单线程的情况下,无论是栈帧太大还是虚拟机栈容量太小,当内存无法再分配的时候,虚拟机抛出的是StackOverflowError异常。
·在多线程下,不断地建立线程可能会产生OutOfMemoryError异常
方法区中的内存溢出
方法区用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
根据以上存放的数据,让其内存溢出只需要大量添加其中的数据
比如比较容易实现的向运行时常量池中的字符串常量池添加字符串常量
我们可以通过String.intern()方法来构建一个运行时常量池的OutOfMemoryError异常。
String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含了一个等于该String对象的字符串,则返回这个String对象,否则,将此String对象包含的字符串添加到常量池中,并返回这个字符串的String对象的引用。如下面代码:
public class 方法区溢出 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
int i = 0;
while (true) {
list.add(String.valueOf(i++).intern());
}
}
}
当然,还可以添加大量的类,比如一些框架大量使用反射,如果不具备卸载类的方法,将很快占满方法区
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- matplotlib基础绘图命令之violinplot
- 性能分析(3)- 短时进程导致用户 CPU 使用率过高案例
- stat 命令家族(1)- 详解 vmstat
- 性能测试必备知识(9)- 深入理解“软中断”
- Vue老项目支持Webpack打包
- 09-4 更改用户密码
- 10-3 信号
- 11-1 环境中存储的是什么?
- 【Rust日报】2020-08-07 无船同志关于Rust未来Generator语法的新尝试
- 11-2 环境是如何建立的
- 11-3 激活修改
- 12-1 定制提示符
- MySQL INSERT的4种形态
- PowerBI DAX 性能优化 高级视图算法 超越经典 性能提升成千上万倍
- Java开发必备 Git 分支开发:规范指南及完全学会Git的24堂课笔记