OutOfMemoryError

2024-09-02 04:12
文章标签 outofmemoryerror

本文主要是介绍OutOfMemoryError,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 堆溢出
  • 虚拟机栈和本地方法栈溢出
  • 方法区和运行时常量池溢出
  • 本机直接内存溢出

Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的几个运行时区域都可能发生OutOfMemoryError(OOM)。

堆溢出

Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径避免垃圾回收机制清除这些对象,那么在对象数量达到最大堆的容量限制后就会产生内存溢出。

package org.hbin.oom;import java.util.ArrayList;
import java.util.List;/*** VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError* @author Haley* @version 1.0* 2024/8/31*/
public class HeapOOMTest {public static void main(String[] args) {List<MyObject> list = new ArrayList<>();while(true) {list.add(new MyObject());}}static class MyObject {}
}
# 运行结果
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid18439.hprof ...
Heap dump file created [27854978 bytes in 0.235 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOf(Arrays.java:3210)at java.util.Arrays.copyOf(Arrays.java:3181)at java.util.ArrayList.grow(ArrayList.java:267)at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241)at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233)at java.util.ArrayList.add(ArrayList.java:464)at org.hbin.oom.HeapOOMTest.main(HeapOOMTest.java:17)

要解决这种瓿,一般是先通过内存分析工具对Dump出来的堆转储快照文件进行分析,确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。
如果是内存泄露,可进一步通过工具查看泄露对象到GC Roots的引用链。于是就能找到泄露对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们的。掌握了泄露对象的类型信息及GC Roots引用链的信息,就可以比较准确的定位出泄露代码的位置。
如果不存在泄露,就是内存中的对象确实还必须存活,那就应当检查虚拟机的堆参数(-Xms和-Xmx)与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过
长,持有状态时间过长的情况,尝试减少程序运行期的内存消耗。

虚拟机栈和本地方法栈溢出

HotSpot虚拟机中并不区分虚拟机栈和本地方法栈。因此,-Xoss参数无效,-Xss参数可以设定栈容量。Java虚拟机规范中描述了栈相关的两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常
  • 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

在单线程中,尝试定义大量的本地变量、大量的方法参数均未产生OutOfMemoryError异常,运行结果都是StackOverflowError异常。代码如下:

package org.hbin.oom;/*** VM args: -Xss256k* @author Haley* @version 1.0* 2024/9/1*/
public class VMStackTest {private int stackLength;public void test() {stackLength ++;test();}public static void main(String[] args) {new VMStackTest().test();}
}

运行结果:

Exception in thread "main" java.lang.StackOverflowErrorat org.hbin.oom.VMStackTest.test(VMStackTest.java:14)at org.hbin.oom.VMStackTest.test(VMStackTest.java:14)at org.hbin.oom.VMStackTest.test(VMStackTest.java:14)……

结论:在单线程中,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackOverflowError异常。

如果不限于单线程,通过不断地建立线程的方式可以产生内存溢出异常。但是这样产生内存溢出与栈空间是否足够大并不存在任何联系,或者准确地说,在这种情况下,为每个线程的栈分配的内存越大,反而越容易产生内存溢出异常。

package org.hbin.oom;/*** VM args: -Xss256k* @author Haley* @version 1.0* 2024/9/1*/
public class VMStackTest2 {private void busy() {while(true);}private void test() {while(true) {new Thread(() -> busy()).start();}}public static void main(String[] args) {new VMStackTest2().test();}
}

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native threadat java.lang.Thread.start0(Native Method)at java.lang.Thread.start(Thread.java:719)at org.hbin.oom.VMStackTest2.test(VMStackTest2.java:17)at org.hbin.oom.VMStackTest2.main(VMStackTest2.java:22)

Windows平台中,该代码执行时可能会导致操作系统假死。如果尝试运行该代码,请务必先保存好当前的工作。

方法区和运行时常量池溢出

String.intern()是一个native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并返回此String对象的引用。
在JDK1.6及之前版本,常量池位于永久代中,可以通过-XX:PermSize 和 -XX:MaxPermSize限制方法区大小,从而间接限制常量池的容量。
在JDK8中,字符串常量池位于堆中,上述参数无效了,可以通过-Xmx指定最大堆空间。

package org.hbin.oom;import java.util.ArrayList;
import java.util.List;/*** JDK1.6 VM args: -XX:PermSize=10M -XX:MaxPermSize=10M* JDK8 VM args: -Xms20m -Xmx20m* @author Haley* @version 1.0* 2024/9/1*/
public class ConstantPoolTest {public static void main(String[] args) {List<String> list = new ArrayList<>();int i = 0;while(true) {list.add(String.valueOf(i++).intern());}}
}

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceededat java.lang.Integer.toString(Integer.java:401)at java.lang.String.valueOf(String.java:3099)at org.hbin.oom.ConstantPoolTest.main(ConstantPoolTest.java:22)

关于常量池,这里有一个有意思的现象:

package org.hbin.oom;/*** @author Haley* @version 1.0* 2024/9/1*/
public class ConstantPoolTest2 {public static void main(String[] args) {String s1 = new StringBuilder("hello").append("world").toString();System.out.println(s1.intern() == s1);String s2 = new StringBuilder("ja").append("va").toString();System.out.println(s2.intern() == s2);}
}

这段代码在不同版本的JDK运行结果可能有差异:

  • JDK 1.6:两个false
  • JDK8:true和false

产生上述差异的原因是:

  • JDK1.6中,intern会把首次遇到的字符串实例复制到永久代中,并返回永久代中这个字符串的引用
  • JDK8中,调用intern时,如果池已经包含与equals(Object)方法确定的相当于此String对象的字符串,则返回来自池的字符串。 否则,此String对象将添加到池中,并返回对此String对象的引用

至于str2比较返回false是因为java这个字符串已经存在于字符串常量池中。测试发现,字符串常量池中还存在其他字符串:java,jar,true,false,main,void,byte,short,int,long,char,boolean,float,double
使用CGLib操作字节码时也可能产生类似问题,代码如下:

package org.hbin.oom;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** VM args: -Xms20m -Xmx20m* @author Haley* @version 1.0* 2024/9/1*/
public class MethodAreaTest {public static void main(String[] args) {while(true) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Object.class);enhancer.setUseCache(false);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] objects, MethodProxy proxy) throws Throwable {return proxy.invokeSuper(obj, objects);}});enhancer.create();}}
}

本机直接内存溢出

DirectMemory容量可以通过-XX:MaxDirectMemorySize指定,默认是Java堆最大值。

package org.hbin.oom;import sun.misc.Unsafe;import java.lang.reflect.Field;/*** VM args: -Xmx20M -XX:MaxDirectMemorySize=10M* @author Haley* @version 1.0* 2024/9/1*/
public class DirectMemoryTest {private static final int _1MB = 1024 * 1024;public static void main(String[] args) throws IllegalAccessException {Field field = Unsafe.class.getDeclaredFields()[0];field.setAccessible(true);Unsafe unsafe = (Unsafe) field.get(null);while(true) {unsafe.allocateMemory(_1MB);}}
}
package org.hbin.oom;import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;/*** VM args: -Xmx20M -XX:MaxDirectMemorySize=10M* @author Haley* @version 1.0* 2024/9/1*/
public class DirectMemoryTest2 {private static final int _1MB = 1024 * 1024;public static void main(String[] args) throws IllegalAccessException {List<ByteBuffer> list = new ArrayList<>();int i = 0;while(true) {ByteBuffer bf = ByteBuffer.allocateDirect(_1MB);list.add(bf);System.out.println(i ++);}}
}

运行结果:

0
1
2
3
4
5
6
7
8
9
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memoryat java.nio.Bits.reserveMemory(Bits.java:695)at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)at org.hbin.oom.DirectMemoryTest2.main(DirectMemoryTest2.java:20)

由DirectMemory导致的内存溢出,明显特征是:在Heap Dump文件中不会看见明显的异常。

这篇关于OutOfMemoryError的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1128960

相关文章

报错:java.lang.OutOfMemoryError: Java heap space

该报错原因是内在不足,设置方法,打开tomcat设置页面,点击open launch configuration,打开配置窗口,在变量参数中加入空格后输入: -Xms512m -Xmx512m -XX:PermSize=512m -XX:MaxPermSize=512m

大数据系列之:OutOfMemoryError: unable to create new native thread

大数据系列之:OutOfMemoryError: unable to create new native thread 问题:环境:原因:解决 问题: 有时候当Java尝试创建一个新的线程时,操作系统会阻止它。在这种情况下,会出现以下错误: java.lang.OutOfMemoryError: unable to create a new native thread 环境:

已解决**Java OutOfMemoryError: GC Overhead Limit Overload - 问题分析与解决方法**

在 Java 中,遇到此java.lang.OutOfMemoryError: GC overhead limit exceeded错误可能是一种具有挑战性的体验,尤其是在处理管理大型数据集或具有长时间运行的进程的应用程序时。此错误表明 Java 虚拟机 (JVM) 花费了太多时间执行垃圾回收 (GC),但无法释放足够的内存来继续处理。 在这篇博客中,我们将深入探讨导致此错误的原因、如何诊断此错

myeclipse解决内存溢出问题 java.lang.OutOfMemoryError: PermGen space

方法: 1、增大jvm(jdk)内存; 2、增大tomcat内存 步骤: 1、增大jvm(jdk)内存:myeclipse中:Windown-->perfermence-->java-->installed jres-->选中要增大内存的jdk-->edit-->在如图所示位置加“ -Xms64m   -Xmx256m”  -->finish即可

运维小技能:通过调整JVM的默认内存配置来解决内存溢出(‌OutOfMemoryError)‌或栈溢出(‌StackOverflowError)‌等错误

文章目录 引言I 调整JVM的默认堆内存配置1.1 java命令启动jar包时配置JVM 的内存参数1.2 基于Tomcat服务器部署的java应用,配置JVM 的内存参数 II 案例: Linux 操作系统设置tomcat的 JVM 的内存参数查找Tomcat位置: 快速定位服务状态和部署位置具体配置步骤 扩展: 监测Nginx访问日志499情况,并做相应动作 引言 问题:

Exception in thread http-bio-23230-exec-609 java.lang.OutOfMemoryError: unable to create new nativ

今天公司123上服务器上的项目都打不开了。看后台报错:   Exception in thread "http-bio-23230-exec-609" java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thre

【Java】已解决:Java.lang.OutOfMemoryError: GC overhead limit exceeded

文章目录 问题背景可能出错的原因错误代码示例正确代码示例注意事项 问题背景 java.lang.OutOfMemoryError: GC overhead limit exceeded 是Java虚拟机(JVM)在运行时遇到的一种内存溢出错误。这种错误通常发生在应用程序的堆内存(Heap Memory)中,当垃圾回收器(Garbage Collector, GC)花费了太多

【Java】已解决:java.lang.OutOfMemoryError: Java heap space

文章目录 一、问题分析背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决Java:java.lang.OutOfMemoryError: Java heap space 一、问题分析背景 在Java开发过程中,有时我们会遇到java.lang.OutOfMemoryError: Java heap space这样的错误。这个错误通常表明Java虚拟机

dex2jar反编译多个dex的apk方法以及常见错误(包括OutOfMemoryError: Java heap space)

工作中常用到反编译apk,但是apk下面不仅仅只有一个dex文件,需求大的情况下会有很多个dex文件,一个个用dex2jar来反编译,再打开代码分析,很麻烦,所以把全部dex文件一次性反编译出来,很有必要。 1,分成多个dex文件的原因 dex文件,是将项目所需全部的class文件合并且压缩到里面,但是单个dex文件引用的方法总数不能超过65536,所以为了解决这个问题,谷歌推出了multid

hadoop Job 运行错误 java.lang.OutOfMemoryError: Java heap space

错误详细内容如下: 2015-12-04 01:21:46,557 FATAL [netty-server-worker-1] org.apache.giraph.graph.GraphTaskManager: uncaughtException: OverrideExceptionHandler on thread netty-server-worker-1, msg = Java heap