模拟Java内存溢出
本文通过修改虚拟机启动参数,来剖析常见的java内存溢出异常(基于jdk1.8)。
修改虚拟机启动参数Java堆溢出虚拟机栈溢出方法区溢出本机直接内存溢出
修改虚拟机启动参数
这里我们使用的是IDEA集成开发环境,选择Run/Debug Configurations
然后选择Configuration,修改VM options配置,就可以修改虚拟机启动参数了,本文的示例代码doc注释部分将会给出需要设置的虚拟机参数。
Java堆溢出
1import java.util.ArrayList;
2import java.util.List;
3
4/**
5 * 堆溢出测试.
6 * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
7 * -XX:HeapDumpPath=/Users/lijl/Desktop
8 *
9 * @author jialin.li
10 * @date 2020-04-08 10:02
11 */
12public class HeapOOM {
13 public static void main(String[] args) {
14 List<HeapOOM> list = new ArrayList<>();
15
16 while (true) {
17 list.add(new HeapOOM());
18 }
19 }
20}
这里简单解释一下代码,我们通过-xms20m -Xmx20m两个参数,限制了Java堆的大小为20MB,不可扩展,后两个参数控制了当出现了OutOfMemoryError时,会Dump出当前内存的堆转储快照,并保存到指定位置中。
接下来我们可以使用jdk自带的VisualVM来打开快照文件。
命令行输入jvisualvm,点击左上角的装入,选中我们dump出来的堆快照文件。
经过重新加载的堆内存记录如下:
这里可以很直观的看出,OutOfMemoryError产生的原因,是HeapOOM这个对象导致的。
解决问题的思路是:首先我们要排除内存泄露,即我们不需要的对象没有被回收掉。我们要找到泄漏的对象是如何与GC Root进行关联的?从而准确定位出泄漏代码的位置,然后进行修改。
如果不是内存泄漏,即堆中的对象必须存活,这个时候,我们可以通过调节虚拟机的堆参数(-Xms -Xmx),适当调大堆内存。但是在此之前,我们一定要检查一下代码是否存在优化的空间,如:是否存在某些对象的生命周期过长?是否可以使用享元模式减少对象数量?等等
虚拟机栈溢出
1/**
2 * 栈溢出测试.
3 * VM Args: Xss128k
4 *
5 * @author jialin.li
6 * @date 2020-04-08 10:02
7 */
8public class StackSOF {
9
10 private static int stackLength = 1;
11
12 public static void main(String[] args) {
13 stackLeak();
14 }
15
16 private static void stackLeak(){
17 try{
18 stackLength++;
19 stackLeak();
20 }catch (StackOverflowError e){
21 System.out.println("stack Length:" +stackLength);
22 e.printStackTrace();
23 }
24 }
25}
StackOverflowError属于比较好排查的一种错误,有错误栈可以阅读,大部分出现这种错误,都是递归程序书写的问题,没有弄清楚什么时候需要return;结束递归。
这里有一个有趣的现象,操作系统给每个进程分配的内存是有限的,在多线程的场景下,如果每个线程分配的栈内存过大,就会导致OOM,这个时候可以适当减少每个线程的栈内存,来解决溢出问题(这可能不是最好的办法,只是因为这是一种比较不符合直觉的解决问题方式,所以这里单独说一下)。
方法区溢出
方法区用来存放Class相关的信息,比如类名、访问修饰符、常量池、字段描述等等。我们可以用运行时产生的大量的类填满方法区,这里我们使用了gclib来操作字节码,maven坐标如下:
1<!-- cglib -->
2<dependency>
3 <groupId>cglib</groupId>
4 <artifactId>cglib</artifactId>
5 <version>2.2.2</version>
6</dependency>
代码:
1import net.sf.cglib.proxy.Enhancer;
2import net.sf.cglib.proxy.MethodInterceptor;
3
4/**
5 * 方法区溢出测试.
6 * VM Args: -XX:PermSize=10k -XX:MaxPermSize=10k
7 *
8 * @author jialin.li
9 * @date 2020-04-08 14:18
10 */
11public class JavaMethodAreaOOM {
12 public static void main(String[] args) {
13 while (true){
14 Enhancer enhancer = new Enhancer();
15 enhancer.setSuperclass(OOMObject.class);
16 enhancer.setUseCache(false);
17 enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invokeSuper(o, args));
18 }
19 }
20
21 static class OOMObject{
22
23 }
24}
如果你用的也是和我一样的jdk1.8,此时我们将没有办法得到OOM,因为在jdk1.8之后,PermGen已经被移除了,所以永久代的参数也被一同移除。方法区的静态变量和常量池并入堆中,而类的元信息放到元空间中,元空间是一块本地内存,所以它的最大可分配空间就是系统内存的最大可用空间。
我们可以将参数改为:VM Args: -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m,再执行上述代码,发现即使在设置了元空间大小的情况下,仍然不会触发OOM,可见元空间可以有效解决方法区OOM问题(会触发元空间的垃圾回收策略)。
本机直接内存溢出
这种情况发生的比较少,直接内存的容量,我们可以通过-XX:MaxDirectMemorySize来指定,如果不指定,默认与堆的最大值一样。我们可以通过Unsafe提供的方法申请本地内存分配。
1import sun.misc.Unsafe;
2import java.lang.reflect.Field;
3
4/**
5 * 本机内存溢出.
6 * VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M
7 *
8 * @author jialin.li
9 * @date 2020-04-08 15:53
10 */
11public class DirectMemoryOOM {
12 private static final int _1MB = 1024 * 1024;
13
14 public static void main(String[] args) throws IllegalAccessException {
15 Field unsafeField = Unsafe.class.getDeclaredFields()[0];
16 unsafeField.setAccessible(true);
17 Unsafe unsafe = (Unsafe) unsafeField.get(null);
18 while (true) {
19 unsafe.allocateMemory(_1MB);
20 }
21 }
22}
如果你是IDE集成开发环境,可能会因为内存不足结束执行程序。
1Process finished with exit code 137
最后,期待您的订阅和点赞,专栏每周都会更新,希望可以和您一起进步,同时也期待您的批评与指正!
模拟Java内存溢出的更多相关文章
- Java内存溢出异常(上)
上一篇文章我们讲了JVM运行时数据区域与内存溢出异常,其中对于内存溢出异常这部分将的不够详细,这篇文章将着重讲解Java内存溢出异常的相关知识.如果有没看过上一篇文章的小伙伴们,请点击Java内存区域 ...
- Java 内存溢出(java.lang.OutOfMemoryError)的常见情况和处理方式总结
最近老是遇见服务器内存溢出的问题,故在网上搜了搜,总结了一些java内存溢出的解决方式 java.lang.OutOfMemoryError这个错误我相信大部分开发人员都有遇到过,产生该错误的原因大都 ...
- java内存溢出分析(二)
我们继续java内存溢出分析(一)的分析,点击Details>按钮,显示如下图,我们发现有一个对象数量达到280370216个,再点击其中的List objects 点击后,显示下图 至此,我们 ...
- Java内存溢出详解
转自:http://elf8848.iteye.com/blog/378805 一.常见的Java内存溢出有以下三种: 1. java.lang.OutOfMemoryError: Java heap ...
- Java内存溢出的详细解决方案
本文介绍了Java内存溢出的详细解决方案.本文总结内存溢出主要有两种情况,而JVM经常调用垃圾回收器解决内存堆不足的问题,但是有时仍会有内存不足的错误.作者分析了JVM内存区域组成及JVM设置虚拟内存 ...
- 老李案例分享:定位JAVA内存溢出
老李案例分享:定位JAVA内存溢出 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loadrunner的培 ...
- java内存溢出问题
相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识. 在解决j ...
- Java内存溢出异常(下)
此篇是上一篇文章Java内存溢出异常(上)的续篇,没有看过的同学,可以先看一下上篇.本篇文章将介绍剩余的两个溢出异常:方法区和运行时常量池溢出. 方法区和运行时常量池溢出 这部分为什么会放在一起呢?在 ...
- java内存溢出的解决思路
原文地址:https://www.cnblogs.com/200911/p/3965108.html 内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能 ...
随机推荐
- java多线程之间的通信
目的 如何让两个线程依次执行? 那如何让 两个线程按照指定方式有序交叉运行呢? 四个线程 A B C D,其中 D 要等到 A B C 全执行完毕后才执行,而且 A B C 是同步运行的 三个运动员各 ...
- Service Mesh - gRPC 本地联调远程服务
Description Service Mesh 架构下,服务间调用会通过服务名(Service Name)互相调用,比如在 Kubernetes .Docker Swarm 集群中,服务 IP 均由 ...
- BTrace实战
BTrace在解决现场问题的时候非常有用. 1.概述 1.1下载 https://github.com/btraceio/btrace,最新版本是1.3.9 目前1.3.x系列最低支持JDK1.7,要 ...
- JavaScript的数组系列
数组 今天逆战班的学习主题关于Javascript的数组,主要有数组的概念.创建.分类.方法.遍历.经典算法...... 一.数组是什么呢?怎么写数组呢?数组有多少种呢? 数组的概念 对象是属性的无序 ...
- Java原来还可以这么学:如何搞定面试中必考的集合类
原创声明 本文作者:黄小斜 转载请务必在文章开头注明出处和作者. 系列文章介绍 本文是<五分钟学Java>系列文章的一篇 本系列文章主要围绕Java程序员必须掌握的核心技能,结合我个人三年 ...
- Python学习笔记.基础一
Python 语言:解释型.交互式.面向对象. Python源代码遵循GPL协议 Python标识符 在python里,标识符有字母.数字.下划线组成. 在python中,所有标识符可以包括英 ...
- pyppeteer基本使用demo
# -*- coding: utf-8 -*- # 类似selenium,支持异步,不需要再单独安装环境,pyppeteer自动安装环境 # 异步await要写到一个函数的内部 from pyppet ...
- 幕布,workflowy的使用技巧
Q: 幕布免费用户导出文档为纯文本或opml: - 将文档Ctrl+C 复制到workflowy: - workflowy可以导出plain-text或opml: 注:已知这样的方法,注释的格式不会被 ...
- JAVAEE学习day03,基本的流程控制
有问题请留言 1.流程控制语句分类 1)顺序控制语句 2)选择结构语句 if... if...else... if...else if... else... for... switch... whil ...
- 金三银四,还在为spring源码发愁吗?bean生命周期,看了这篇就够了
第一,这绝对是一个面试高频题. 比第一还重要的第二,这绝对是一个让人爱恨交加的面试题.为什么这么说?我觉得可以从三个方面来说: 先说会不会.看过源码的人,这个不难:没看过源码的人,无论是学.硬背.还是 ...