那些年,我们遇到的OOM

智码云帆
• 阅读 2495

JVM内存模型

先聊聊jvm内存模型,在网上找到一张有直接内存的图片,方便后面讨论

那些年,我们遇到的OOM

这张图真是常看常新,今天我们从内存溢出的角度重新再审视一遍。
方法区,也称非堆,hotspot中,1.7叫perm区,1.8叫元空间,因此这个区域溢出,1.7就是OutOfMemoryError: PermGen space,1.8是OutOfMemoryError: Metaspace

堆溢出比较常见,OutOfMemoryError: Java heap space

虚拟机栈的溢出是 StackOverflowError

本地方法栈会出现 java.lang.OutOfMemoryError : unable to create new native Thread

直接内存会抛出 OutOfMemoryError: Direct buffer memory

以下将逐个撑爆这些空间

先来撑爆虚拟机栈 StackOverflowError

不停地递归调用,jvm不得不在虚拟机栈上分配栈帧空间,从而导致sofe,感兴趣的还可以查看一下递归的次数,可能通过-Xss进行配置,通过命令jinfo -flag ThreadStackSize [pid]可以查看栈空间大小的配置

public class StackOverflowErrorDemo {
    private static void test() {
        test();
    }

    public static void main(String[] args) {
        test();
    }
}

再来撑爆堆吧 OutOfMemoryError: Java heap space

先指定-Xmx8m -Xms8m,然后直接在堆中生成一个8m字节的数组,可以直接看到效果

public class JavaHeapSpaceDemo {
    public static void main(String[] args) {
        byte[] bytes = new byte[8 * 1014 * 1024];
    }
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.meituan.waimai.jvm.JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:11)

方法区有分说

如果是1.7,会出现PemGen space, 这要求我们不断往生成类的信息。由于1.7,字符串常量池已经挪到堆中了,所以使用String.intern()并不会导致perm区溢出

public class OOMDemo {
    public static void main(String[] args) {
        String str = "hello";
        List<String> list = Lists.newArrayList();
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            str += (i + "").intern();
            list.add(str);
        }
    }
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at com.meituan.waimai.jvm.JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:18)

那要怎么做呢,这里我们需要动态地生成一些类,直到把perm区撑爆,jvm参数配置:-XX:MaxPermSize=8m

    static class OOMTest{}

    public static void main(final String[] args) {
        int i = 0;

        try {
            for (; ; ) {
                i++;
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(OOMTest.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        return methodProxy.invokeSuper(o, args);
                    }
                });
                enhancer.create();
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}
Caused by: java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
... 8 more

将jdk改为1.8,-XX:MaxMetaspaceSize=20m 重新执行上述程序,结果变成Metaspace溢出

java.lang.OutOfMemoryError: Metaspace
at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)
at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
at com.meituan.waimai.jvm.JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:36)

该对直接内存动手的

配置jvm参数,-XX:MaxDirectMemorySize=8m,然后分配9m的直接内存:

public class DirectMemoryDemo {
    public static void main(String[] args) {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(9 * 1024 * 1024);
    }
}

可以看到

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:694)
at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
at com.meituan.waimai.jvm.DirectMemoryDemo.main(DirectMemoryDemo.java:13)

本地方法栈 unable to create new native Thread

不停地创建java线程,就可以把本地方法栈撑爆

public class NativeThreadDemo {
    public static void main(String[] args) {

        for (; ; ) {
            new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:717)
at com.meituan.waimai.jvm.NativeThreadDemo.main(NativeThreadDemo.java:21)

这个程序跑完忘了关,结果一会mac重启了,非常可怕。。。。
关于unable to create new native Thread,知乎上有个非常深刻地讨论
https://www.zhihu.com/questio...

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java 内存管理 堆和栈的理解
在JVM中,内存分为两个部分,Stack(栈)和Heap(堆),这里,我们从JVM的内存管理原理的角度来认识Stack和Heap,并通过这些原理认清Java中静态方法和静态属性的问题。一般,JVM的内存分为两部分:Stack和Heap。Heap(堆)是JVM的内存数据区。Heap的管理很复杂,每次分配不定长的内存空间,专门用来保存对象
Wesley13 Wesley13
3年前
JAVA工程师成神道路
一、基础篇1.1JVM1.1.1.Java内存模型,Java内存管理,Java堆和栈,垃圾回收http://www.jcp.org/en/jsr/detail?id133http://ifeve.com/jmmfaq/1.1.2.了解JVM各种参数及调优1.1.3.
Wesley13 Wesley13
3年前
Java工程师成神之路~
一、基础篇1.1JVM1.1.1.Java内存模型,Java内存管理,Java堆和栈,垃圾回收http://www.jcp.org/en/jsr/detail?id133http://ifeve.com/jmmfaq/1.1.2.了解JVM各种参数及调优1.1.3.
Easter79 Easter79
3年前
Tomcat中JVM内存溢出及合理配置
Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个Java虚拟机。Tomcat的内存溢出本质就是JVM内存溢出,所以在本文开始时,应该先对JavaJVM有关内存方面的知识进行详细介绍。一、JavaJVM内存介绍JVM管理两种类型的内存,堆和非堆。按照官方的说法:“Java虚拟机具有一个堆,堆是运行时数据区域,
Wesley13 Wesley13
3年前
JDK8中JVM堆内存划分
一:JVM中内存JVM中内存通常划分为两个部分,分别为堆内存与栈内存,栈内存主要用运行线程方法存放本地暂时变量与线程中方法运行时候须要的引用对象地址。JVM全部的对象信息都存放在堆内存中。相比栈内存,堆内存能够所大的多,所以JVM一直通过对堆内存划分不同的功能区块实现对堆内存中对象管理。堆内存不够最常见的错误就是OOM(OutOf
Wesley13 Wesley13
3年前
Java工程师成神之路
一、基础篇1.1JVM1.1.1.Java内存模型,Java内存管理,Java堆和栈,垃圾回收http://www.jcp.org/en/jsr/detail?id133http://ifeve.com/jmmfaq/1.1.2.了解JVM各种参数及调优
Stella981 Stella981
3年前
JVM 面试
1、内存模型以及分区,需要详细到每个区放什么。通俗的说,Java虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。JVM主要管理两种类型内存:堆和非堆,堆内存(Heap Memory)是在Java虚拟机启动时创建,非堆内存(NonheapMemory)是在JVM堆之外的内存。简单来说,堆是Java代码可及的内
Wesley13 Wesley13
3年前
Java中的OutOfMemoryError的各种情况及解决和JVM内存结构
在JVM中内存一共有3种:Heap(堆内存),NonHeap(非堆内存)\3\和Native(本地内存)。\1\堆内存是运行时分配所有类实例和数组的一块内存区域。非堆内存包含方法区和JVM内部处理或优化所需的内存,存放有类结构(如运行时常量池、字段及方法结构,以及方法和构造函数代码)。本地内存是由操作系统管理的虚拟内存。当一个应用内存不足时
Stella981 Stella981
3年前
JVM
  最近又鼓捣jvm,然后结合着VisualVM监控、jvm的书,对jvm又有一些理解体会,今天主要聊聊jvm的线程模型,内存模型,以及跟开发比较相关的GC。  线程模型,jvm的线程就对应os的线程,据说linux的kernel3.x之后,也出现了OS内核线程支持,之前实现是轻量级进程,比如你线程搞多了,或者kernel内存留少了,unab
Wesley13 Wesley13
3年前
Java8内存模型
<divclass"htmledit\_views"<h1<aname"t0"</a一、JVM内存模型</h1<p</p<p<spanstyle"fontfamily:'宋体';"内存空间</span(RuntimeDataArea)中可以按照是否线程共享分为两块,线程共享的是方法区(MethodArea)和堆
Wesley13 Wesley13
3年前
NIO直接缓冲区与非直接缓冲区
非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在jvm内存中。直接缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率。!这里写图片描述(https://static.oschina.net/uploads/img/201709/06150240_bPZ3.jpg)!
智码云帆
智码云帆
Lv1
一个人真好,不用迁就别人,别人也不用忍我。
文章
4
粉丝
0
获赞
0