JVM内存溢出

在看这个之前,最好了解一下GC日志

1.栈溢出

参数:-Xss1m, 默认值以官网为准:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

-Xsssize
Sets the thread stack size (in bytes). Append the letter k or K to indicate KB, m or M to indicate MB, g or G to indicate GB. The default value depends on the platform:
Linux/ARM (32-bit): 320 KB
Linux/i386 (32-bit): 320 KB
Linux/x64 (64-bit): 1024 KB
OS X (64-bit): 1024 KB
Oracle Solaris/i386 (32-bit): 320 KB
Oracle Solaris/x64 (64-bit): 1024 KB
The following examples set the thread stack size to 1024 KB in different units:
-Xss1m
-Xss1024k
-Xss1048576
This option is equivalent to -XX:ThreadStackSize.

HotSpot 版本中栈的大小是固定的,是不支持拓展的。
java.lang.StackOverflowError: 一般的方法调用是很难出现的,如果出现了可能会是无限递归。
虚拟机栈带给我们的启示:方法的执行因为要打包成栈桢,所以天生要比实现同样功能的循环慢,所以树的遍历算法中:递归和非递归(循环来实现)都有存在的意义。
递归代码简洁,非递归代码复杂但是速度较快。
测试代码:

package sandwich;

/**
 * @author 公众号:IT三明治
 * @date 2021/3/7
 */
public class StickOverFlow {

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

    private static void A() {
        A();
    }
}

//error msg:
Exception in thread "main" java.lang.StackOverflowError
	at ex2.oom.StackOverFlow.king(StackOverFlow.java:10)
	at ex2.oom.StackOverFlow.king(StackOverFlow.java:10)
	at ex2.oom.StackOverFlow.king(StackOverFlow.java:10)
	at ex2.oom.StackOverFlow.king(StackOverFlow.java:10)
	at ex2.oom.StackOverFlow.king(StackOverFlow.java:10)

OutOfMemoryError:不断建立线程,JVM 申请栈内存,机器没有足够的内存。(一般演示不出,演示出来机器也死了)
栈区的总空间 JVM 没有办法去限制的,因为 JVM 在运行过程中会有线程不断的运行,没办法限制,所以只限制单个虚拟机栈的大小
-Xss1m其实只限制单个虚拟机栈的大小
以下异常比较常见,也算是栈溢出的一种*
创建本地线程内存溢出
例如我机器有4G,我通过VM options设定堆2G,方法区1800M
那我机器还剩余200m
我同时跑200线程可能需要消耗 200*1m(默认栈帧大小1M)=200M
这时如果我再增加线程,就会出现栈溢出。因为我们只能配置单个虚拟机栈大小,不能配置总栈帧大小
测试代码

package sandwich.test2;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * @author 公众号:IT三明治
 * @date 2021/4/17
 * -Xms15000m -Xmx15000m -XX:MaxMetaspaceSize=500M -XX:+PrintGCDetails
 */
public class UnableCreateNativeThreadError {
    public static void main(String[] args) {
        while (true) {
            Executor pool = Executors.newCachedThreadPool();
            pool.execute(()-> System.out.println("test"));
        }
    }
}

三明治电脑16G内存,这里直接给堆分配了15G, 方法分配500M,剩下的等新线程的虚拟机栈慢慢吃完
以下是异常信息

test
An unrecoverable stack overflow has occurred.
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x0000000065014d37, pid=59380, tid=0x0000000000024694
#
# JRE version: Java(TM) SE Runtime Environment (8.0_191-b12) (build 1.8.0_191-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# test
test
test
test
test
test
test
test
test
test
test
Vtest
test
test
test
test
test
  [jvm.dll+0x214d37]test
An unrecoverable stack overflow has occurred.
[thread 149204 also had an error]
test
test
test
test
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 sandwich.test2.UnableCreateNativeThreadError.main(UnableCreateNativeThreadError.java:15)

Process finished with exit code -1073741571 (0xC00000FD)

2.堆溢出

内存溢出:申请内存空间,超出最大堆内存空间。
如果是内存溢出,则通过 调大 -Xms,-Xmx 参数。 如果不是内存泄漏,就是说内存中的对象却是都是必须存活的,那么应该检查 JVM 的堆参数设置,与机器的内存对比,看是否还有可以调整的空间, 再从代码上检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等情况,尽量减少程序运行时的内存消耗。
测试1

package sandwich.test2;

/**
 * @author 公众号:IT三明治
 * @date 2021/4/14
 * VM Args:-Xms10m -Xmx10m -XX:+PrintGCDetails
 * 堆内存溢出(直接溢出)
 */
public class HeapOom {

    public static void main(String[] args) {
        //15m的数组(堆)
        String[] strings = new String[15*1000*1000];
    }
}

VM Args:-Xms10m -Xmx10m -XX:+PrintGCDetails
+PrintGCDetails: 这里是打印GC详情信息

异常信息:

[GC (Allocation Failure) [PSYoungGen: 1453K->488K(2560K)] 1453K->656K(9728K), 0.0017629 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 488K->504K(2560K)] 656K->672K(9728K), 0.0019136 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 504K->0K(2560K)] [ParOldGen: 168K->589K(7168K)] 672K->589K(9728K), [Metaspace: 2944K->2944K(1056768K)], 0.0061170 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 589K->589K(9728K), 0.0004814 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 589K->572K(7168K)] 589K->572K(9728K), [Metaspace: 2944K->2944K(1056768K)], 0.0063580 secs] [Times: user=0.06 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 2560K, used 161K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 7% used [0x00000000ffd00000,0x00000000ffd284e0,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
 ParOldGen       total 7168K, used 572K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 7% used [0x00000000ff600000,0x00000000ff68f100,0x00000000ffd00000)
 Metaspace       used 3065K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 332K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at sandwich.test2.HeapOom.main(HeapOom.java:13)
Process finished with exit code 1

测试2

package sandwich.test2;

import java.util.LinkedList;
import java.util.List;

/**
 * @author 公众号:IT三明治
 * @date 2021/4/14
 * VM Args:-Xms10m -Xmx10m -XX:+PrintGCDetails     堆的大小10M
 * 造成一个堆内存溢出(分析下JVM的分代收集)
 * GC调优---生产服务器推荐开启(默认是关闭的)
 * -XX:+HeapDumpOnOutOfMemoryError
 */
public class HeapOom2 {

    public static void main(String[] args) throws InterruptedException {
        // list   当前虚拟机栈(局部变量表)中引用的对象
        List<Object> list = new LinkedList<>();
        int i =0;
        while(true){
            i++;
            if(i%1000==0) {
                Thread.sleep(10);
            }
            // 不能回收2,  优先回收再来抛出异常。
            list.add(new Object());
        }
    }
}

异常信息

[GC (Allocation Failure) [PSYoungGen: 2048K->496K(2560K)] 2048K->1036K(9728K), 0.0024622 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2544K->496K(2560K)] 3084K->3068K(9728K), 0.0098904 secs] [Times: user=0.09 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 2513K->496K(2560K)] 5085K->4676K(9728K), 0.0067160 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 2544K->480K(2560K)] 6724K->6564K(9728K), 0.0089384 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
......
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7034K->7034K(7168K)] 9082K->9082K(9728K), [Metaspace: 3721K->3721K(1056768K)], 0.0447096 secs] [Times: user=0.23 sys=0.00, real=0.05 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7035K->7035K(7168K)] 9083K->9083K(9728K), [Metaspace: 3721K->3721K(1056768K)], 0.0501366 secs] [Times: user=0.39 sys=0.02, real=0.05 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7037K->7037K(7168K)] 9085K->9085K(9728K), [Metaspace: 3721K->3721K(1056768K)], 0.0441021 secs] [Times: user=0.38 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7038K->7038K(7168K)] 9086K->9086K(9728K), [Metaspace: 3721K->3721K(1056768K)], 0.0446760 secs] [Times: user=0.25 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7039K->7039K(7168K)] 9087K->9087K(9728K), [Metaspace: 3721K->3721K(1056768K)], 0.0429731 secs] [Times: user=0.38 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7040K->7040K(7168K)] 9088K->9088K(9728K), [Metaspace: 3721K->3721K(1056768K)], 0.0446995 secs] [Times: user=0.36 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7042K->7042K(7168K)] 9090K->9090K(9728K), [Metaspace: 3721K->3721K(1056768K)], 0.0448296 secs] [Times: user=0.36 sys=0.00, real=0.05 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->0K(2560K)] [ParOldGen: 7058K->907K(7168K)] 9106K->907K(9728K), [Metaspace: 3746K->3746K(1056768K)], 0.0074949 secs] [Times: user=0.09 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 2560K, used 53K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 2% used [0x00000000ffd00000,0x00000000ffd0d408,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 7168K, used 907K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 12% used [0x00000000ff600000,0x00000000ff6e2f30,0x00000000ffd00000)
 Metaspace       used 3752K, capacity 4536K, committed 4864K, reserved 1056768K
  class space    used 411K, capacity 428K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at sandwich.test2.HeapOom2.main(HeapOom2.java:26)

可以看到开始是普通的GC,后面就是不断的Full GC。当GC占据了98%的资源,回收不足2%的时候,这个回收已经失去意义了。就会抛出OutOfMemoryError: GC overhead limit exceeded

3.方法区溢出

3.1 运行时常量池溢出

3.2 方法区中保存的 Class 对象没有被及时回收掉或者 Class 信息占用的内存超过了我们配置。

注意 Class 要被回收,条件比较苛刻(仅仅是可以,不代表必然,因为还有一些参数可以进行控制)
1、该类所有的实例都已经被回收,也就是堆中不存在该类的任何实例。
2、 加载该类的 ClassLoader 已经被回收。
3、 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
代码示例
cglib 是一个强大的,高性能,高质量的 Code 生成类库,它可以在运行期扩展 Java 类与实现 Java 接口。 CGLIB 包的底层是通过使用一个小而快的字节码处理框架 ASM,来转换字节码并生成新的类。除了 CGLIB 包,脚本语言例如 Groovy 和 BeanShell, 也是使用 ASM 来生成 java 的字节码。当然不鼓励直接使用 ASM,因为它要求你必须对 JVM 内部结构包括 class 文件的格式和指令集都很熟悉。
先添加cglib依赖

<dependencies>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
    </dependencies>

再执行以下代码

package sandwich.test2;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * @author 公众号:IT三明治
 * @date 2021/4/17
 * 方法区导致的内存溢出
 * VM Args: -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M -XX:+PrintGCDetails
 * MetaspaceSize是方法区大小
 */
public class MethodAreaOutOfMemory {
    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(TestObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
                    return arg3.invokeSuper(arg0, arg2);
                }
            });
            enhancer.create();
        }
    }

    public static class TestObject {
        private double a = 1.11;
        private Integer b = 123456;
    }
}

以上是通过cglib不断加载test类。让方法区内存超出所设定的10M
异常信息

[GC (Allocation Failure) [PSYoungGen: 65024K->2672K(75776K)] 65024K->2688K(249344K), 0.0041476 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 67696K->2728K(75776K)] 67712K->2752K(249344K), 0.0040184 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Metadata GC Threshold) [PSYoungGen: 45809K->3624K(75776K)] 45833K->3656K(249344K), 0.0033195 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Metadata GC Threshold) [PSYoungGen: 3624K->0K(75776K)] [ParOldGen: 32K->3475K(101376K)] 3656K->3475K(177152K), [Metaspace: 9623K->9623K(1058816K)], 0.0162852 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
[GC (Last ditch collection) [PSYoungGen: 0K->0K(101376K)] 3475K->3475K(202752K), 0.0005170 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Last ditch collection) [PSYoungGen: 0K->0K(101376K)] [ParOldGen: 3475K->1921K(194560K)] 3475K->1921K(295936K), [Metaspace: 9623K->9623K(1058816K)], 0.0235838 secs] [Times: user=0.13 sys=0.00, real=0.02 secs] 
Heap
 PSYoungGen      total 101376K, used 2610K [0x00000007ab580000, 0x00000007b3f80000, 0x00000007ffe00000)
  eden space 100864K, 2% used [0x00000007ab580000,0x00000007ab80cab8,0x00000007b1800000)
  from space 512K, 0% used [0x00000007b3f00000,0x00000007b3f00000,0x00000007b3f80000)
  to   space 10752K, 0% used [0x00000007b2a80000,0x00000007b2a80000,0x00000007b3500000)
 ParOldGen       total 194560K, used 1921K [0x0000000702400000, 0x000000070e200000, 0x00000007ab580000)
  object space 194560K, 0% used [0x0000000702400000,0x00000007025e07a0,0x000000070e200000)
 Metaspace       used 9658K, capacity 10122K, committed 10240K, reserved 1058816K
  class space    used 858K, capacity 881K, committed 896K, reserved 1048576K
Exception in thread "main" net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
	at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:348)
	at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
	at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:117)
	at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)
	at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
	at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
	at sandwich.test2.MethodAreaOutOfMemory.main(MethodAreaOutOfMemory.java:26)
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:459)
	at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:339)
	... 6 more
Caused by: java.lang.OutOfMemoryError: Metaspace
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	... 11 more
Process finished with exit code 1

4.本机直接内存溢出

直接内存是未被jvm虚拟化的内存(堆外内存),直接内存的容量可以通过 MaxDirectMemorySize 来设置(默认与堆内存最大值一样),所以也会出现 OOM 异常; 由直接内存导致的内存溢出,一个比较明显的特征是在 HeapDump 文件中不会看见有什么明显的异常情况,如果发生了 OOM,同时 Dump 文件很小,可 以考虑重点排查下直接内存方面的原因。
测试代码

package sandwich.test2;

import java.nio.ByteBuffer;

/**
 * @author 公众号:IT三明治
 * @date 2021/4/17
 * VM Args:-XX:MaxDirectMemorySize=10m -XX:+PrintGCDetails
 * 限制最大直接内存大小10m
 */
public class DirectOom {
    public static void main(String[] args) {
        //直接分配12M的直接内存
        ByteBuffer buffer = ByteBuffer.allocateDirect(12*1024*1204);
    }
}

指定直接内存为10M,但是在代码中分配12M直接内存。就会出现直接内存溢出OutOfMemoryError: Direct buffer memory
以下是异常信息

[GC (System.gc()) [PSYoungGen: 5207K->744K(75776K)] 5207K->752K(249344K), 0.0009499 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 744K->0K(75776K)] [ParOldGen: 8K->598K(173568K)] 752K->598K(249344K), [Metaspace: 3022K->3022K(1056768K)], 0.0055451 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
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 sandwich.test2.DirectOom.main(DirectOom.java:14)
Heap
 PSYoungGen      total 75776K, used 3251K [0x000000076b780000, 0x0000000770c00000, 0x00000007c0000000)
  eden space 65024K, 5% used [0x000000076b780000,0x000000076baace70,0x000000076f700000)
  from space 10752K, 0% used [0x000000076f700000,0x000000076f700000,0x0000000770180000)
  to   space 10752K, 0% used [0x0000000770180000,0x0000000770180000,0x0000000770c00000)
 ParOldGen       total 173568K, used 598K [0x00000006c2600000, 0x00000006ccf80000, 0x000000076b780000)
  object space 173568K, 0% used [0x00000006c2600000,0x00000006c2695940,0x00000006ccf80000)
 Metaspace       used 3242K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 352K, capacity 388K, committed 512K, reserved 1048576K

5.超出交换区内存溢出

在Java应用程序启动过程中,可以通过-Xmx和其他类似的启动参数限制指定的所需的内存。而当JVM所请求的总内存大于可用物理内存的情况下,操作系统开始将内容从内存转换为硬盘。

一般来说JVM会抛出Out of swap space错误,代表应用程序向JVM native heap请求分配内存失败并且native heap也即将耗尽时,错误消息中包含分配失败的大小(以字节为单位)和请求失败的原因。
解决办法: 增加系统交换区的大小,我个人认为,如果使用了交换区,性能会大大降低,不建议采用这种方式,生产环境尽量避免最大内存超过系统的物理内存。其次,去掉系统交换区,只使用系统的内存,保证应用的性能。

6.数组超限内存溢出

有的时候会碰到这种内存溢出的描述Requested array size exceeds VM limit,一般来说java对应用程序所能分配数组最大大小是有限制的,只不过不同的平台限制有所不同,但通常在1到21亿个元素之间。当Requested array size exceeds VM limit错误出现时,意味着应用程序试图分配大于Java虚拟机可以支持的数组。JVM在为数组分配内存之前,会执行特定平台的检查:分配的数据结构是否在此平台是可寻址的。
测试代码

package sandwich.test2;

/**
 * @author 公众号:IT三明治
 * @date 2021/4/17
 */
public class ArrayLimitOomError {
    public static void main(String[] args) {
        for (int i = 3; i>=0; i--) {
            try {
                int[] arr = new int[Integer.MAX_VALUE-i];
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }
}

异常信息

[GC (Allocation Failure) [PSYoungGen: 5207K->776K(75776K)] 5207K->784K(2846208K), 0.0011478 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 776K->712K(75776K)] 784K->720K(2846208K), 0.0014875 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 712K->0K(75776K)] [ParOldGen: 8K->635K(70656K)] 720K->635K(146432K), [Metaspace: 3210K->3210K(1056768K)], 0.0142392 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(75776K)] 635K->635K(2846208K), 0.0004173 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(75776K)] [ParOldGen: 635K->617K(92672K)] 635K->617K(168448K), [Metaspace: 3210K->3210K(1056768K)], 0.0144618 secs] [Times: user=0.08 sys=0.00, real=0.01 secs] 
java.lang.OutOfMemoryError: Java heap space
	at sandwich.test2.ArrayLimitOomError.main(ArrayLimitOomError.java:11)
[GC (Allocation Failure) [PSYoungGen: 1300K->96K(106496K)] 1918K->713K(2876928K), 0.0005783 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 96K->128K(124416K)] 713K->745K(2894848K), 0.0006590 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 128K->0K(124416K)] [ParOldGen: 617K->612K(111616K)] 745K->612K(236032K), [Metaspace: 3236K->3236K(1056768K)], 0.0153015 secs] [Times: user=0.09 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(183296K)] 612K->612K(2953728K), 0.0003688 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(183296K)] [ParOldGen: 612K->612K(137728K)] 612K->612K(321024K), [Metaspace: 3236K->3236K(1056768K)], 0.0113537 secs] [Times: user=0.00 sys=0.02, real=0.01 secs] 
java.lang.OutOfMemoryError: Java heap space
	at sandwich.test2.ArrayLimitOomError.main(ArrayLimitOomError.java:11)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
	at sandwich.test2.ArrayLimitOomError.main(ArrayLimitOomError.java:11)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
	at sandwich.test2.ArrayLimitOomError.main(ArrayLimitOomError.java:11)
Heap
 PSYoungGen      total 183296K, used 9114K [0x000000076b780000, 0x0000000776b80000, 0x00000007c0000000)
  eden space 182272K, 5% used [0x000000076b780000,0x000000076c066928,0x0000000776980000)
  from space 1024K, 0% used [0x0000000776a80000,0x0000000776a80000,0x0000000776b80000)
  to   space 1024K, 0% used [0x0000000776980000,0x0000000776980000,0x0000000776a80000)
 ParOldGen       total 2770432K, used 612K [0x00000006c2600000, 0x000000076b780000, 0x000000076b780000)
  object space 2770432K, 0% used [0x00000006c2600000,0x00000006c26991d0,0x000000076b780000)
 Metaspace       used 3243K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 352K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 0

解决方法:
数组长度要在平台允许的长度范围之内。不过这个错误一般少见的,主要是由于Java数组的索引是int类型。 Java中的最大正整数为2 ^ 31 - 1 = 2,147,483,647。 并且平台特定的限制可以非常接近这个数字,例如:我的环境上(64位,运行Jdk1.8)可以初始化数组的长度高达2,147,483,645(Integer.MAX_VALUE-2)。若是在将数组的长度再增加1达到nteger.MAX_VALUE-1会出现的OutOfMemoryError。

7.系统杀死进程内存溢出

在描述该问题之前,先熟悉一点操作系统的知识:操作系统是建立在进程的概念之上,这些进程在内核中作业,其中有一个非常特殊的进程,称为“内存杀手(Out of memory killer)”。当内核检测到系统内存不足时,OOM killer被激活,检查当前谁占用内存最多然后将该进程杀掉。

一般Out of memory:Kill process or sacrifice child错会在当可用虚拟虚拟内存(包括交换空间)消耗到让整个操作系统面临风险时,会被触发。在这种情况下,OOM Killer会选择“流氓进程”并杀死它。
测试代码

package sandwich.test2;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 公众号:IT三明治
 * @date 2021/4/18
 */
public class OsKillerOomError {
    public static void main(String[] args) {
        List<int[]> list = new ArrayList<>();
        for (int i = 10000; i<100000; i++) {
            try {
                list.add(new int[100000000]);
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }
}

异常信息

[GC (Allocation Failure) [PSYoungGen: 6508K->1064K(75776K)] 2740883K->2735447K(2846208K), 0.0092089 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 1064K->0K(75776K)] [ParOldGen: 2734383K->2735345K(2770432K)] 2735447K->2735345K(2846208K), [Metaspace: 3715K->3715K(1056768K)], 0.0599107 secs] [Times: user=0.13 sys=0.03, real=0.06 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(75776K)] 2735345K->2735345K(2846208K), 0.0084789 secs] [Times: user=0.11 sys=0.00, real=0.01 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(75776K)] [ParOldGen: 2735345K->2735292K(2770432K)] 2735345K->2735292K(2846208K), [Metaspace: 3715K->3715K(1056768K)], 0.0182811 secs] [Times: user=0.11 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 1300K->96K(75776K)] 2736593K->2735388K(2846208K), 0.0075106 secs] [Times: user=0.11 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) java.lang.OutOfMemoryError: Java heap space
	at sandwich.test2.OsKillerOomError.main(OsKillerOomError.java:15)
[PSYoungGen: 96K->0K(75776K)] [ParOldGen: 2735292K->2735286K(2770432K)] 2735388K->2735286K(2846208K), [Metaspace: 3740K->3740K(1056768K)], 0.0164984 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(93696K)] 2735286K->2735286K(2864128K), 0.0064980 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(93696K)] [ParOldGen: 2735286K->2735286K(2770432K)] 2735286K->2735286K(2864128K), [Metaspace: 3740K->3740K(1056768K)], 0.0112876 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 3318K->32K(93696K)] 2738605K->2735318K(2864128K), 0.0074204 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) java.lang.OutOfMemoryError: Java heap space
	at sandwich.test2.OsKillerOomError.main(OsKillerOomError.java:15)
[PSYoungGen: 32K->0K(93696K)] [ParOldGen: 2735286K->2735286K(2770432K)] 2735318K->2735286K(2864128K), [Metaspace: 3740K->3740K(1056768K)], 0.0162669 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(109056K)] 2735286K->2735286K(2879488K), 0.0065482 secs] [Times: user=0.09 sys=0.00, real=0.01 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(109056K)] [ParOldGen: 2735286K->2735286K(2770432K)] 2735286K->2735286K(2879488K), [Metaspace: 3740K->3740K(1056768K)], 0.0120665 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 2171K->64K(110080K)] 2737457K->2735350K(2880512K), 0.0065148 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) java.lang.OutOfMemoryError: Java heap space
	at sandwich.test2.OsKillerOomError.main(OsKillerOomError.java:15)
[PSYoungGen: 64K->0K(110080K)] [ParOldGen: 2735286K->2735285K(2770432K)] 2735350K->2735285K(2880512K), [Metaspace: 3740K->3740K(1056768K)], 0.0159117 secs] [Times: user=0.11 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(143872K)] 2735285K->2735285K(2914304K), 0.0074721 secs] [Times: user=0.09 sys=0.00, real=0.01 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(143872K)] [ParOldGen: 2735285K->2735285K(2770432K)] 2735285K->2735285K(2914304K), [Metaspace: 3740K->3740K(1056768K)], 0.0121244 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 2857K->32K(143872K)] 2738143K->2735317K(2914304K), 0.0067953 secs] [Times: user=0.11 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 32K->0K(143872K)] [ParOldGen: 2735285K->2735285K(2770432K)] 2735317K->2735285K(2914304K), [Metaspace: 3740K->3740K(1056768K)], 0.0155739 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
java.lang.OutOfMemoryError: Java heap space
	at sandwich.test2.OsKillerOomError.main(OsKillerOomError.java:15)
[GC (Allocation Failure) [PSYoungGen: 0K->0K(172032K)] 2735285K->2735285K(2942464K), 0.0076039 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(172032K)] [ParOldGen: 2735285K->2735285K(2770432K)] 2735285K->2735285K(2942464K), [Metaspace: 3740K->3740K(1056768K)], 0.0122402 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 3420K->0K(172032K)] 2738705K->2735285K(2942464K), 0.0065794 secs] [Times: user=0.11 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 0K->0K(172032K)] [ParOldGen: 2735285K->2735285K(2770432K)] 2735285K->2735285K(2942464K), [Metaspace: 3740K->3740K(1056768K)], 0.0106526 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
java.lang.OutOfMemoryError: Java heap space
[GC (Allocation Failure) [PSYoungGen: 0K->0K(206336K)] 2735285K->2735285K(2976768K), 0.0071458 secs] [Times: user=0.11 sys=0.00, real=0.01 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(206336K)] [ParOldGen: 2735285K->2735285K(2770432K)] 2735285K->2735285K(2976768K), [Metaspace: 3740K->3740K(1056768K)], 0.0099758 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 4106K->0K(206336K)] 2739391K->2735285K(2976768K), 0.0073891 secs] [Times: user=0.11 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 0K->0K(206336K)] [ParOldGen: 2735285K->2735285K(2770432K)] 2735285K->2735285K(2976768K), [Metaspace: 3741K->3741K(1056768K)], 0.0105378 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
...

解决方法

虽然增加交换空间的方式可以缓解Java heap space异常,还是建议最好的方案就是升级系统内存,让java应用有足够的内存可用,就不会出现这种问题

如何减少内存溢出

1.第三方jar包要慎重引入,坚决去掉没有用的jar,如果是通过maven或者gradle引入 依赖,要优化好依赖,提高编译的速度和系统的占用内存。

2.对于大的对象或者大量的内存申请,要进行优化,大的对象要分片处理,提高处理性能,减少对象生命周期。

3.尽量固定线程的数量,保证线程占用内存可控,同时需要大量线程时,要优化好操作系统的最大可打开的连接数。

4.对于递归调用,也要控制好递归的层级,不要太高,超过栈的深度。

5.分配给栈的内存并不是越大越好,因为栈内存越大,线程多,留给堆的空间就不多了,容易抛出OOM。JVM的默认参数一般情况没有问题(包括递归)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT三明治

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值