在这里插入图片描述

前言

除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能。以下代码测试环境为JDK 1.8

Java Heap 溢出

Java heap space

// -Xmx8m
public class TestOomTooManyObject {
    public static void main(String[] args) {
        String name = "hello";
        for (int i = 0; i < 10000000; i++) {
            name += name;
        }
        System.out.println(name);
    }
}

输出结果:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOfRange(Arrays.java:3664)
	at java.lang.String.<init>(String.java:207)
	at java.lang.StringBuilder.toString(StringBuilder.java:407)
	at com.fast.summary.jvm.oom.TestOomTooManyObject.main(TestOomTooManyObject.java:12)

Metaspace

// -XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=10m
// 模拟不断生成类, 但类无法卸载的情况
public class TestOomTooManyClass {
    static String base = "string";
    public static void main(String[] args) {
        testDynamicCreateClass();
    }

    private static void testDynamicCreateClass() {
        AtomicInteger c = new AtomicInteger();
        while (true) {
            try (FileReader reader = new FileReader("script")) {
                GroovyShell shell = new GroovyShell();
                shell.evaluate(reader);
                System.out.println(c.incrementAndGet());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

输出结果:

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at org.codehaus.groovy.util.SingleKeyHashMap.getOrPut(SingleKeyHashMap.java:67)
	at org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.<init>(MetaMethodIndex.java:85)
	at groovy.lang.MetaClassImpl.<init>(MetaClassImpl.java:193)
	at groovy.lang.MetaClassImpl.<init>(MetaClassImpl.java:221)
	at groovy.lang.MetaClassImpl.<init>(MetaClassImpl.java:233)
	at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createNormalMetaClass(MetaClassRegistry.java:166)
	at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:156)
	at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:139)
	at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.<init>(MetaClassRegistryImpl.java:133)
	at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.<init>(MetaClassRegistryImpl.java:86)
	at groovy.lang.GroovySystem.<clinit>(GroovySystem.java:37)
	at org.codehaus.groovy.runtime.InvokerHelper.<clinit>(InvokerHelper.java:88)
	at groovy.lang.GroovyObjectSupport.getDefaultMetaClass(GroovyObjectSupport.java:46)
	at groovy.lang.GroovyObjectSupport.<init>(GroovyObjectSupport.java:32)
	at groovy.lang.Binding.<init>(Binding.java:33)
	at groovy.lang.GroovyShell.<init>(GroovyShell.java:63)
	at com.fast.summary.jvm.oom.TestOomTooManyClass.testDynamicCreateClass(TestOomTooManyClass.java:21)
	at com.fast.summary.jvm.oom.TestOomTooManyClass.main(TestOomTooManyClass.java:14)

Direct buffer memory

// -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
public class TestOomDirectBufferMemory {
    public static void main(String[] args) {
        System.out.println("查看配置的本地内存maxDirectMemory:" + (sun.misc.VM.maxDirectMemory() / (double) 1024 / 1024) + "MB");
        //停顿一下方便看效果
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //-XX:MaxDirectMemorySize=5m 本地内存配置的是5MB,这里实际使用的是6MB
        ByteBuffer bb = ByteBuffer.allocateDirect(6 * 1024 * 1024);
    }
}

StackOverflowError

/**
 * StackOverflowError :栈溢出
 * 什么时候会让 Java Method Stack 栈溢出啊?
 * 栈的基本特点就是 FILO(First In Last Out),
 * 如果 in 的太多而 out 的太少,就好 overflow 了。
 * 而 Java Method Stack 的功能就是保存每一次函数调用时的“现场”,
 * 即为入栈,函数返回就对应着出栈,所以函数调用的深度越大,栈就变得越大,足够大的时候就会溢出。
 * 所以模拟 Java Method Stack 溢出,只要不断递归调用某一函数就可以。
 * @author qinfuxiang
 */
public class TestStackOverflowError {
    private static int stackLength = 0;

    public void stackOverflow() {
        ++stackLength;
        stackOverflow();
    }

    public static void main(String[] args) {
        TestStackOverflowError test = new TestStackOverflowError();
        try {
            test.stackOverflow();
        } catch (Throwable e) {
            System.out.println("stack length: " + test.stackLength);
            throw e;
        }
    }
}

运行结果:

Exception in thread "main" java.lang.StackOverflowError
	at com.fast.summary.jvm.oom.TestStackOverflowError.stackOverflow(TestStackOverflowError.java:18)
	at com.fast.summary.jvm.oom.TestStackOverflowError.stackOverflow(TestStackOverflowError.java:18)
	at com.fast.summary.jvm.oom.TestStackOverflowError.stackOverflow(TestStackOverflowError.java:18)
	at com.fast.summary.jvm.oom.TestStackOverflowError.stackOverflow(TestStackOverflowError.java:18)
	at com.fast.summary.jvm.oom.TestStackOverflowError.stackOverflow(TestStackOverflowError.java:18)
	at com.fast.summary.jvm.oom.TestStackOverflowError.stackOverflow(TestStackOverflowError.java:18)
	at com.fast.summary.jvm.oom.TestStackOverflowError.stackOverflow(TestStackOverflowError.java:18)
	at com.fast.summary.jvm.oom.TestStackOverflowError.stackOverflow(TestStackOverflowError.java:18)
	at com.fast.summary.jvm.oom.TestStackOverflowError.stackOverflow(TestStackOverflowError.java:18)
	

Executors 默认的几种会出现OOM

newFixedThreadPool

在这里插入图片描述

  • LinkedBlockingQueue为无界队列
// -Xmx8m
private static void fixedThreadPool() {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        LoggerUtils.get().debug("begin...");
        while (true) {
            executor.submit(() -> {
                try {
                    LoggerUtils.get().debug("send sms...");
                    TimeUnit.SECONDS.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.util.concurrent.Executors.callable(Executors.java:407)
	at java.util.concurrent.FutureTask.<init>(FutureTask.java:152)
	at java.util.concurrent.AbstractExecutorService.newTaskFor(AbstractExecutorService.java:87)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:111)
	at com.fast.summary.jvm.oom.TestOomThreadPool.fixedThreadPool(TestOomThreadPool.java:32)

newSingleThreadExecutor

在这里插入图片描述

  • LinkedBlockingQueue为无界队列
// -Xmx8m -XX:-UseGCOverheadLimit
private static void singleThreadExecutor() {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        LoggerUtils.get().debug("begin...");
        while (true) {
            executor.submit(() -> {
                try {
                    LoggerUtils.get().debug("send sms...");
                    TimeUnit.SECONDS.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.concurrent.Executors.callable(Executors.java:407)
	at java.util.concurrent.FutureTask.<init>(FutureTask.java:152)
	at java.util.concurrent.AbstractExecutorService.newTaskFor(AbstractExecutorService.java:87)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:111)
	at java.util.concurrent.Executors$DelegatedExecutorService.submit(Executors.java:678)
	at com.fast.summary.jvm.oom.TestOomThreadPool.singleThreadExecutor(TestOomThreadPool.java:47)
	at com.fast.summary.jvm.oom.TestOomThreadPool.main(TestOomThreadPool.java:22)

newCachedThreadPool

在这里插入图片描述

  • 会不断创建线程
static ExecutorService executorService = Executors.newCachedThreadPool();

    static class Task implements Runnable {
        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void scheduledThreadPool() {
        ExecutorService executor = Executors.newScheduledThreadPool(2);
        LoggerUtils.get().debug("begin...");
        while (true) {
            executor.submit(() -> {
                try {
                    LoggerUtils.get().debug("send sms...");
                    TimeUnit.SECONDS.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }

运行结果:

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 java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:957)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1378)
	at com.fast.summary.jvm.oom.TestOomThreadPool.cachedThreadPool(TestOomThreadPool.java:60)
	at com.fast.summary.jvm.oom.TestOomThreadPool.main(TestOomThreadPool.java:23)
Java HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated

newScheduledThreadPool

在这里插入图片描述

  • DelayedWorkQueue 是无界队列

运行结果:

java.lang.OutOfMemoryError: Java heap spacejava.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:530)
	at java.util.concurrent.ScheduledThreadPoolExecutor.submit(ScheduledThreadPoolExecutor.java:632)
	at com.fast.summary.jvm.oom.TestOomThreadPool.scheduledThreadPool(TestOomThreadPool.java:81)
	at com.fast.summary.jvm.oom.TestOomThreadPool.main(TestOomThreadPool.java:24)

github代码: https://github.com/fafeidou/fast-summary/tree/master/fast-summary-jvm/src/main/java/com/fast/summary/jvm/oom

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐