内存溢出(OOM)分析
2022/9/17 5:17:35
本文主要是介绍内存溢出(OOM)分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
当JVM内存不足时,会抛出java.lang.OutOfMemoryError. 主要的OOM类型右:- Java heap space:堆空间不足
- GC overhead limit exceeded : GC开销超出限制
- Permgen space:永久代内存不足
- Metaspace:元空间内存不足
- Unable to create new native thread:无法创建新的本地线程
- Out of swap space? : 交换空间内存不足
- Kill process or sacrifice child
Java heap space:堆空间不足
- 通用解决方案:通过-Xmx设置更大的堆内存【该方式可能只是延迟报错的发生,如果不能从根本上找到原因,报错还是可能会发生】
- 进一步原因分析及解决方案:
- 流量/数据量峰值 : 可以考虑添加机器资源,或者做限流
- 内存泄漏 : 需要找到持有的对象,修改代码
- 创建了一个超大对象(通常是一个大数组) : 可以进行业务切分
- 代码示例
- 内存泄漏【-Xmx10m】
package oom; import java.util.HashMap; import java.util.Map; /** * 内存泄露 */ public class JavaHeapSpace2 { public static void main(String[] args) { Map<Key,String> map = new HashMap<>(); while (true) { Key key = new Key(); if(!map.containsKey(key)) { map.put(key, "Java Overhead"); System.out.println(key); } } } } class Key { }View Code
oom.Key@2ef70cb4 ...... oom.Key@457298d0 oom.Key@484b94f2 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.HashMap.resize(HashMap.java:704) at java.util.HashMap.putVal(HashMap.java:663) at java.util.HashMap.put(HashMap.java:612) at oom.JavaHeapSpace2.main(JavaHeapSpace2.java:16)View Result
-
- 创建了一个超大对象
package oom; import java.lang.management.ManagementFactory; import java.util.List; public class JavaHeapSpace { private static final int SIZE = 12 * 1024 * 2014; public static void main(String[] args) { List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments(); System.out.println("JVM Arguments : " + inputArguments); int[] arr = new int[SIZE]; } }View Code
JVM Arguments : [-Xmx12m, -Dfile.encoding=GBK] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at oom.JavaHeapSpace.main(JavaHeapSpace.java:13) Process finished with exit code 1View Result[-Xmx12m]
JVM Arguments : [-Xmx35m,-Dfile.encoding=GBK] Process finished with exit code 0View Result[-Xmx35m]
GC overhead limit exceeded : GC开销超出限制
默认情况下,当应用程序花费超过98%的时间用来做GC并且回收了不到2%的堆内存时,会抛出java.lang.OutOfMemoryError:GC overhead limit exceeded错误。 此类问题的原因与解决方案跟 Java heap space 非常类似,可以参考上文- 代码演示【使用默认的VM配置】
package oom; import java.util.HashMap; import java.util.Map; public class JavaHeapSpace2 { public static void main(String[] args) { Map<Key,String> map = new HashMap<>(); while (true) { Key key = new Key(); if(!map.containsKey(key)) { map.put(key, "Java Overhead"); System.out.println(key); } } } } class Key { }View Code
oom.Key@61f7f66c oom.Key@1da844d ...... oom.Key@792b37e7 oom.Key@3d8151c0Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at java.nio.CharBuffer.wrap(CharBuffer.java:373) at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:265) at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)View Result
Permgen space:永久代内存不足
- 背景:永久代主要存储类的信息,比如:类加载引用、运行时常量池、字段、方法等。因此,Permgen的大小取决于被加载类的数量及类的大小。
- 原因:
- 加载了太多的类
- 加载了超大类
- 注意:JDK8已经完全移除永久代空间,取而代之的是元空间(Metaspace)
Metaspace:元空间内存不足
- 背景:Metaspace存储类的元数据信息
- 原因:
- 加载了太多的类
- 加载了超大类
- 解决方案
- 调整-XX:MaxMetaspaceSize参数
- 删除-XX:MaxMetaspaceSize参数,解除限制【默认是没有限制的。默认情况下,对于64位服务器端JVM,MetaspaceSize默认大小是21M(初始限制值),一旦达到这个限制值,FullGC将被触发进行类卸载,并且这个限制值将会被重置,新的限制值依赖于Metaspace的剩余容量。如果没有足够空间被释放,这个限制值将会上升。】
- 代码演示【JDK1.8】
package oom; import javassist.CannotCompileException; import javassist.ClassPool; import java.lang.management.ManagementFactory; import java.util.List; public class Metaspace{ public static void main(String[] args) throws CannotCompileException { List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments(); System.out.println("JVM Arguments : " + inputArguments); for (int i = 0; i < 100000000; i++) { ClassPool.getDefault().makeClass("User" + i).toClass(); } } }View Code
JVM Arguments : [-XX:MaxMetaspaceSize=35m, -Dfile.encoding=GBK] Exception in thread "main" javassist.CannotCompileException: by java.lang.OutOfMemoryError: Metaspace at javassist.ClassPool.toClass(ClassPool.java:1099) at javassist.ClassPool.toClass(ClassPool.java:1042) at javassist.ClassPool.toClass(ClassPool.java:1000) at javassist.CtClass.toClass(CtClass.java:1224) at oom.Permgen.main(Permgen.java:16) Caused by: java.lang.OutOfMemoryError: Metaspace at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:763) at java.lang.ClassLoader.defineClass(ClassLoader.java:642) 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 javassist.ClassPool.toClass2(ClassPool.java:1112) at javassist.ClassPool.toClass(ClassPool.java:1093) ... 4 moreView Result
Unable to create new native thread:无法创建新的本地线程
- 背景:每个线程都需要一定的内存空间,当JVM向底层操作系统请求创建一个新的native线程时,如果没有足够的资源分配就会报这个错误
- 原因分析及解决方案:
- 线程数超过了OS最大线程数ulimit限制 : 调高 OS 层面的线程最大数 - 执行 ulimia-a 查看最大线程数限制,使用 ulimit-u xxx 调整最大线程数限制
- 线程数超过了本地线程最大数:限制线程池大小 ;
- native内存不足 :
- 使用 -Xss 参数减少线程栈的大小
- 升级配置,为机器提供更多的内存
- 代码示例
package oom; public class UnableCreateThread { public static void main(String[] args) { while (true) { new Thread(() -> { try { Thread.sleep(100000000); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } } }View Code
# # A fatal error has been detected by the Java Runtime Environment: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000006a674447, pid=18648, tid=0x0000000000067b68 # # JRE version: Java(TM) SE Runtime Environment (8.0_171-b11) (build 1.8.0_171-b11) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode windows-amd64 compressed oops) # Problematic frame: # V [jvm.dll+0x214447][thread 424944 also had an error] An unrecoverable stack overflow has occurred. [thread 424964 also had an error] An unrecoverable stack overflow has occurred. [thread 424984 also had an error] [thread 424992 also had an error] An unrecoverable stack overflow has occurred. [thread 424988 also had an error] 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 oom.UnableCreateThread.main(UnableCreateThread.java:14)View Result
Out of swap space? : 交换空间内存不足
- 背景:虚拟内存(Virtual Memory)由物理内存(Physical Memory)和交换空间(Swap Space)两部分组成。在JVM请求的总内存大于可用物理内存的情况下,操作系统会将内存中的数据交换到磁盘上去。当交换空间也将耗尽时就会报 Outof swap space? 错误。
- 原因分析及解决方案:【往往是由操作系统级别的问题引起的】
- 操作系统配置的交换空间不足 :
- 加交换空间【对交换空间运行垃圾回收算法会使GC暂停的时间增加几个数量级,因此使用增加交换空间的方法】
- 升级机器以包含更多内存
- 系统上的另一个进程消耗所有内存资源:如果应用部署在JVM需要同其他进程激烈竞争获取资源的物理机上,建议将服务隔离到单独的虚拟机中
- 本地内存泄漏导致应用程序失败: 优化应用程序以减少其内存占用
- 背景:操作系统是建立在进程的概念之上,这些进程在内核中作业,其中有一个非常特殊的进程,名叫“内存杀手(Out of memory killer)”。当内核检测到系统内存不足时,OOM killer被激活,然后选择一个进程杀掉。
- 原因分析:程序占用大量系统内存导致其他进程没有可用内存
- 解决方案:
- 调整OOM Killer配置
- 升级机器以包含更多内存
- 《Java内存溢出(OOM)异常完全指南》
- 《OOM 9种常见原因及解决方案》
这篇关于内存溢出(OOM)分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-15Springboot应用的多环境打包学习
- 2024-10-15Springboot应用的生产发布学习
- 2024-10-15创建Springboot项目学习:新手入门教程
- 2024-10-15创建springboot项目学习:新手入门教程
- 2024-10-14Java创业学习:新手入门教程
- 2024-10-14JAVA创业学习:新手入门教程
- 2024-10-14Java创业学习:从零开始的Java编程教程
- 2024-10-14Java对接阿里云智能语音服务学习
- 2024-10-14JAVA对接阿里云智能语音服务学习教程
- 2024-10-14Java副业学习:从入门到实战的简单教程