博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java内存溢出的典型场景测试
阅读量:4113 次
发布时间:2019-05-25

本文共 2830 字,大约阅读时间需要 9 分钟。

引言:本文是阅读经典的《深入理解Java虚拟机》后,对第二章的内存溢出异常情况进行总结,通过实际代码实现来验证知识点的正确性。

1.堆内存溢出

Java的堆用于存储对象实例,只要不断地创建对象,并且保证GC root 到对象之间有可达路径,就无法被收集器回收。

在java工程的目录下新建一个类,这里命名为MemoryController,代码如下:

@RestControllerpublic class MemoryController {
private List
heapList = new ArrayList<>(); /** * -Xmx32M -Xms32M * */ @GetMapping("/heap") public String heap() {
long i=1l; while(true) {
heapList.add(new TestEntity(i++, UUID.randomUUID().toString())); } }}

运行大约十分钟之后,结果显示是堆内存溢出:

在这里插入图片描述
《深入理解Java虚拟机》书中给出这种情况的解决思路:
在这里插入图片描述
(1)如果是内存泄露,查GCRoot引用链,打印出引用链信息,就基本可以定位泄露代码的为位置。
(2)如果不存在泄露,也就是内存中的对象确实都必须存活,就检查虚拟机的堆参数。看看-Xmx和-Xms在机器物理内存的基础上可否再调大。
具体方法可见另一篇博客:
不过大多数情况都是在代码层面上检查是否有对象生命周期过长或关联过多,尝试减少程序运行期的内存消耗即可。

2. 元数据区溢出

《深入理解Java虚拟机》中称作方法区,JDK1.7进行了“去永久代”之后,就成了元数据区,用于存Class对象,一个类要被垃圾收集器回收的条件很苛刻,尤其是在框架越来越多的情况下。因此这里创建大量不回收的类对象。

首先,在工程的pom.xml中引入asm的jar包帮助实现动态生成类:

asm
asm
3.3.1

创建动态生成类Metaspace,代码如下:

public class Metaspace extends ClassLoader {
public static Collection
> createClasses() {
// 类持有 List
> classes = new ArrayList
>(); // 循环1000w次生成1000w个不同的类。 for (int i = 0; i < 10000000; ++i) {
ClassWriter cw = new ClassWriter(0); // 定义一个类名称为Class{i},它的访问域为public,父类为java.lang.Object,不实现任何接口 cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null); // 定义构造函数
方法 MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "
", "()V", null, null); // 第一个指令为加载this mw.visitVarInsn(Opcodes.ALOAD, 0); // 第二个指令为调用父类Object的构造函数 mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "
", "()V"); // 第三条指令为return mw.visitInsn(Opcodes.RETURN); mw.visitMaxs(1, 1); mw.visitEnd(); Metaspace test = new Metaspace(); byte[] code = cw.toByteArray(); // 定义类 Class
exampleClass = test.defineClass("Class" + i, code, 0, code.length); classes.add(exampleClass); } return classes; }}

在之前的MemoryController里加metaspace()方法

@RestControllerpublic class MemoryController {
List
> classlist = new ArrayList<>(); /** * -XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M */ @GetMapping("/metaspace") public String metaspace() {
long i=1l; while(true) {
//动态生成class classlist.addAll(Metaspace.createClasses()); } }}

为了能够马上看到结果,这里手动把JVM的元数据去参数修改一下,IDEA里的修改如下图

在这里插入图片描述
很快直接元数据区溢出,并且spring直接定位到了导致溢出的代码行
在这里插入图片描述

本文主要测试了两种典型的内存溢出,还有栈溢出(递归调用可能溢出)以及本机溢出以可测试,但是出现情况较少,就没有做实际的测试了。

转载地址:http://nbrsi.baihongyu.com/

你可能感兴趣的文章
第四章 - 程序计数器
查看>>
第七章 - 本地方法栈
查看>>
第十一章 - 直接内存
查看>>
JDBC核心技术 - 上篇
查看>>
JDBC核心技术 - 下篇
查看>>
一篇搞懂Java反射机制
查看>>
一篇彻底搞懂Java注解与枚举类
查看>>
【2021-MOOC-浙江大学-陈越、何钦铭-数据结构】树
查看>>
MySQL主从复制不一致的原因以及解决方法
查看>>
RedisTemplate的key默认序列化器问题
查看>>
序列化与自定义序列化
查看>>
ThreadLocal
查看>>
从Executor接口设计看设计模式之最少知识法则
查看>>
OKhttp之Call接口
查看>>
application/x-www-form-urlencoded、multipart/form-data、text/plain
查看>>
关于Content-Length
查看>>
WebRequest post读取源码
查看>>
使用TcpClient可避免HttpWebRequest的常见错误
查看>>
EntityFramework 学习之一 —— 模型概述与环境搭建 .
查看>>
C# 发HTTP请求
查看>>