📅  最后修改于: 2020-04-04 08:00:38             🧑  作者: Mango
在Java中,所有对象都存储在堆中。使用new运算符分配它们。Java中的OutOfMemoryError异常如下所示:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
通常,当Java虚拟机由于内存不足而无法分配对象,并且垃圾回收器无法再提供更多内存时,将引发此错误。
OutOfMemoryError通常意味着您做错了什么,要么将对象保持太长时间,要么试图一次处理太多数据。有时,这表示您无法控制问题,例如缓存字符串的第三方库或部署后无法清理的应用程序服务器。有时,它与堆上的对象无关。
该java.lang.OutOfMemoryError异常,也可以通过本机库的代码时,抛出本地分配不能满足(例如,如果交换空间不足)。让我们了解可能发生OutOfMemory错误的各种情况。
根本原因
为了找到原因,该异常的文本末尾包含了一条详细的消息。让我们检查所有错误。
// Java程序展示
// Heap error
import java.util.*;
public class Heap {
static List list = new ArrayList();
public static void main(String args[]) throws Exception
{
Integer[] array = new Integer[10000 * 10000];
}
}
当您执行上面的代码时,您可能希望它永远运行而不会出现任何问题。结果,随着时间的流逝,随着泄漏代码的不断使用,“缓存”结果最终会占用大量Java堆空间,并且当泄漏的内存填满堆区域中的所有可用内存而Garbage Collection无法清理它,将抛出java.lang.OutOfMemoryError:Java heap space。
预防措施:在监视对象待定终结中检查如何监视待定结的对象。
// Java程序,展示
// 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 Wrapper运行该程序,则输出将如下所示:
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:-UseGCOverheadLimit将其关闭。
// Java程序展示
// 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 Space。
预防措施:如果在应用程序启动期间由于PermGen耗尽导致OutOfMemoryError,则解决方案很简单。该应用程序仅需要更多空间来将所有类加载到PermGen区域,因此我们只需要增加其大小即可。为此,请更改您的应用程序启动配置并添加-XX:MaxPermSize 参数类似于以下示例:
Java -XX:MaxPermSize = 512m com.saket.demo.Permgen
// Java展示
// 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启动程序,然后在Mac OS X上,我的Java 1.8.0_05死于大约70,000个类。
预防措施:如果已在命令行上设置MaxMetaSpaceSize,请增加其值。MetaSpace是从与Java堆相同的地址空间分配的。减少Java堆的大小将为MetaSpace提供更多空间。如果Java堆中有多余的可用空间,这只是一个正确的权衡。
// Java程序展示请求的数组大小超出了VM限制
import java.util.*;
public class GFG {
static List list = new ArrayList();
public static void main(String args[]) throws Exception
{
Integer[] array = new Integer[10000 * 10000];
}
}
java.lang.OutOfMemoryError:由于以下两种情况之一,可能会显示请求的数组大小超出VM限制:
预防措施:引发此错误消息时,VM会调用致命错误处理机制(即,它会生成致命错误日志文件,其中包含有关崩溃时线程,进程和系统的有用信息)。在本机堆耗尽的情况下,日志中的堆内存和内存映射信息可能会很有用
// Java程序展示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.0_45 –创建#2031线程后,JVM死亡
预防措施:使用操作系统的本机实用程序来进一步诊断问题。