在看这个之前,最好了解一下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的默认参数一般情况没有问题(包括递归)