4年前 (2020-11-26)  jvm |   抢沙发  387 
文章评分 0 次,平均分 0.0

在Java中,所有对象都存储在堆中。它们使用新操作员分配。Java中的OutOfMemoryError异常如下所示:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

通常,当Java虚拟机由于内存不足而无法分配对象,并且垃圾回收器无法提供更多内存时,会引发此错误。

outofmemory

OutOfMemoryError通常意味着您做了一些错误的事情,要么是将对象抓得太久,要么是一次试图处理太多的数据。有时,它表示您无法控制的问题,例如缓存字符串的第三方库,或部署后不清理的应用程序服务器。有时,它与堆上的对象无关。

这个java.lang.OutOfMemoryError当无法满足本机分配时(例如,如果交换空间很低),本机库代码也会引发异常。让我们了解可能发生OutOfMemory错误的各种情况。

outofmemory 症状或根本原因?

为了找到原因,异常的文本在末尾包含了详细的消息。让我们检查一下所有的错误。

错误1–Java堆空间:

此错误是由于应用程序过度使用终结器而导致的。如果类具有finalize方法,则该类型的对象在垃圾收集时不会回收其空间。取而代之的是,在垃圾回收之后,这些对象将排队等待最后确定,这将在以后发生。

  • 终结器由为终结队列提供服务的守护程序线程执行。
  • 如果线程终结器无法结束,则会抛出此异常的内存终结器。
  • 问题也可以简单到配置问题,指定的堆大小(或默认大小,如果未指定)对于应用程序来说不足。
// Java program to illustrate 
// Heap error 
import java.util.*; 
  
public class Heap { 
    static List<String> list = new ArrayList<String>(); 
  
public static void main(String args[]) throws Exception 
    { 
        Integer[] array = new Integer[10000 * 10000]; 
    } 
} 

当您执行上面的代码时,您可能希望它永远运行而不会出现任何问题。结果随着时间的推移,随着泄漏代码的不断使用,“缓存”结果最终会消耗大量的Java堆空间,并且当java 内存泄露的内存填满堆区域中的所有可用内存,而垃圾回收无法清理它时,则java.lang.OutOfMemoryError:引发Java堆空间。

预防:在“监视挂起终结的对象”中,检查如何监视终结挂起的对象。

错误2–超出GC开销限制:

此错误表示垃圾回收器一直在运行,而Java程序的进度非常缓慢。在垃圾收集之后,如果Java进程花费超过98%的时间进行垃圾收集,并且如果它回收的堆少于2%,并且到目前为止已经连续进行了5次(编译时常量)的垃圾收集,则java.lang.OutOfMemoryError被抛出。

此异常通常会引发,因为活动数据量几乎不能放入Java堆中,因为Java堆中几乎没有用于新分配的可用空间。

// Java program to illustrate 
// GC Overhead limit exceeded 
import java.util.*; 
  
public class Wrapper { 
public static void main(String args[]) throws Exception 
    { 
        Map m = new HashMap(); 
        m = System.getProperties(); 
        Random r = new Random(); 
        while (true) { 
            m.put(r.nextInt(), "randomValue"); 
        } 
    } 
} 

如果使用java-Xmx100m-XX:+UseParallelGC包装器运行此程序,则输出如下:

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.lang.Integer.valueOf(Integer.java:832)
    at Wrapper.main(error.java:9)

预防:增加堆大小,并使用命令行标志-XX:-usegoverheadlimit将其关闭。

错误3–抛出Permgen空间:

Java内存被分成不同的区域。所有这些区域的大小,包括permgen区域,都是在JVM启动期间设置的。如果不自己设置大小,则将使用特定于平台的默认值。

这个java.lang.OutOfMemoryError:PermGen space error表示内存中的永久生成区域已用尽。

// Java program to illustrate 
// Permgen Space error 
import javassist.ClassPool; 
  
public class Permgen { 
    static ClassPool classPool = ClassPool.getDefault(); 
  
public static void main(String args[]) throws Exception 
    { 
        for (int i = 0; i < 1000000000; i++) { 
            Class c = classPool.makeClass(com.saket.demo.Permgen" + i).toClass(); 
            System.out.println(c.getName()); 
        } 
    } 
} 

在上面的示例代码中,代码迭代循环并在运行时生成类。类生成的复杂性由Javassist库处理。

运行上面的代码将继续生成新的类并将它们的定义加载到Permgen空间,直到空间被充分利用并且java.lang.OutOfMemoryError:Permgen空间被抛出。

预防:当在应用程序启动期间由于PermGen耗尽而导致OutOfMemoryError时,解决方案很简单。应用程序只需要更多的空间来将所有类加载到PermGen区域,所以我们只需要增加它的大小。为此,请更改应用程序启动配置,并添加(或增加)与以下示例类似的-XX:MaxPermSize参数:

java -XX:MaxPermSize=512m com.saket.demo.Permgen

错误4–元空间:

Java类元数据在本机内存中分配。如果类元数据的元空间用尽,则java.lang.OutOfMemoryError引发具有detail元空间的异常。

可用于类元数据的元空间量受参数MaxMetaSpaceSize的限制,该参数在命令行中指定。当类元数据所需的本机内存量超过MaxMetaSpaceSize时,一个java.lang.OutOfMemoryError引发具有detail元空间的异常。

// Java program to illustrate 
// Metaspace error 
import java.util.*; 
  
public class Metaspace { 
    static javassist.ClassPool cp = javassist.ClassPool.getDefault(); 
  
public static void main(String args[]) throws Exception 
    { 
        for (int i = 0; i < 100000; i++) { 
            Class c = cp.makeClass("com.saket.demo.Metaspace" + i).toClass(); 
        } 
    } 
} 

此代码将继续生成新类并将其定义加载到Metaspace,直到空间被充分利用,并且java.lang.OutOfMemoryError:抛出Metaspace。当以-XX:MaxMetaspaceSize=64m启动时,在MacOSX上,我的Java1.8.0_05会在大约70000个类的加载下消亡。

预防:如果在命令行上设置了MaxMetaSpaceSize,请增加其值。元空间是从与Java堆相同的地址空间分配的。减小Java堆的大小将为MetaSpace提供更多的可用空间。只有在Java堆中有多余的可用空间时,这才是一个正确的权衡。关于metaspace的内存溢出排查可以参考这篇文章:https://javakk.com/160.html

错误5–请求的数组大小超过VM限制:

此错误表示应用程序试图分配大于堆大小的数组。例如,如果应用程序尝试分配1024 MB的阵列,但最大堆大小为512 MB,则OutOfMemoryError将抛出“请求的数组大小超过VM限制”。

// Java program to illustrate 
// Requested array size 
// exceeds VM limit error 
import java.util.*; 
  
public class GFG { 
    static List<String> list = new ArrayList<String>(); 
  
public static void main(String args[]) throws Exception 
    { 
        Integer[] array = new Integer[10000 * 10000]; 
    } 
} 

这个java.lang.OutOfMemoryError:请求的阵列大小超过VM限制可能会由于以下任一情况而出现:

  • 你的数组变得太大,最终的大小介于平台限制和Integer.MAX_INT
  • 您故意尝试分配大于2^31-1个元素的数组来尝试限制。

错误6–Out of swap space交换空间不足:

当来自本机堆的分配失败并且本机堆可能即将耗尽时,会发生此明显的异常。错误指示失败的请求的大小(以字节为单位)和内存请求的原因。通常原因是报告分配失败的源模块的名称,尽管有时这是实际原因。

java.lang.OutOfMemoryError:Out of swap space error 交换空间不足错误通常由操作系统级别的问题引起,例如:

  • 操作系统配置的交换空间不足。
  • 系统上的另一个进程正在消耗所有内存资源。

预防:当抛出此错误消息时,VM调用致命错误处理机制(即,它生成一个致命错误日志文件,其中包含有关崩溃时线程、进程和系统的有用信息)。在本机堆耗尽的情况下,日志中的堆内存和内存映射信息可能很有用

错误7-reason stack_trace_with_native_method:

每当抛出此错误消息(reason stack_trace_with_native_method),则会打印顶部帧为本机方法的堆栈跟踪,然后这表示本机方法遇到了分配失败。这条消息与前一条消息的区别在于,分配失败是在Java本机接口(JNI)或本机方法中检测到的,而不是在JVM代码中检测到的。

// Java program to illustrate 
// new native thread error 
import java.util.*; 
  
public class GFG { 
public static void main(String args[]) throws Exception 
    { 
        while (true) { 
            new Thread(new Runnable() 
            { 
                public void run() 
                { 
                    try
                    { 
                        Thread.sleep(1000000000); 
        } 
        catch (InterruptedException e) 
        { 
        } 
    } 
            }).start(); 
   } 
  } 
} 

确切的本机线程限制取决于平台,例如,测试Mac OS X发现:

64位Mac OS X 10.9、Java 1.7_45–JVM在创建2031个线程后死亡

预防:使用操作系统的本机实用程序来进一步诊断问题。有关可用于各种操作系统的工具的更多信息,请参阅本机操作系统工具。

更多其他的内存溢出分析以及outofmemory怎么解决可以参考:https://javakk.com/883.html

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/894.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册