发生以下情况之一时,可能会引发OutOfMemoryError
:
- JVM耗尽了本机内存
- Java堆内存不足
- PermGen或Metaspace内存不足
- JVM花太多时间试图收集垃圾
通常可以从错误消息中OutOfMemoryError
出OutOfMemoryError
的根本原因。 让我们研究每种情况的细节。
JVM耗尽了本机内存
这基本上意味着分配给JVM的内存量已用完。 32位JVM的最大进程大小约为3.5 – 4 GB。 如果超过它,将OutOfMemoryError
。 即使在64位JVM中,当JVM请求更多内存时,操作系统也可能根本没有足够的内存。 请看以下片段:
for (int i = 0; true; ++i) {
new Thread() {
public void run() {
try {
Thread.sleep(1000000);
} catch(InterruptedException e) { }
}
}.start();
System.out.println("Thread"; + i + "created");
}
在我的笔记本上(带有Java 1.8.0_112的64位Mac OS X 10.11.6),在创建2023个线程之后,JVM崩溃了:
Thread 2021 created
Thread 2022 created
Thread 2023 created
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
Java堆内存不足
这个很明显。 分配了太多对象,因此它们不适合为JVM配置的堆空间。 增大堆大小听起来像是一种解决方案,但是如果它是由内存泄漏引起的,则只会推迟OutOfMemoryError
。 错误消息非常清楚:
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
PermGen或Metaspace内存不足
PermGen(Java 7和更早版本)的最大大小有限。 这意味着如果加载了太多的类,PermGen可能会填满,并且将引发OutOfMemoryError
。 增加最大PermGen大小应该会有所帮助。 Java 8没有PermGen,但是有Metaspace。 默认情况下,它具有无限制的最大大小,因此,只要不通过MaxMetaspaceSize
标志设置限制,就不应引发该错误。 要诊断由PermGen或Metaspace引起的OutOfMemoryError
,应检查错误消息:
Exception in thread “main” java.lang.OutOfMemoryError: PermGen space
Exception in thread “main” java.lang.OutOfMemoryError: Metaspace
JVM花太多时间试图收集垃圾
这是最棘手的问题–当GC花太多时间收集垃圾而导致的结果太少且进一步的应用程序执行毫无意义时,将引发OutOfMemoryError
。 换句话说,必须满足以下所有条件:
- GC中花费了超过98%的时间(98%是默认值,可以被
GCTimeLimit=N
覆盖) - 在完整GC期间,只有不到2%的堆被回收(再次,2%是默认值,可以被
GCHeapFreeLimit=N
覆盖)。 - 前面提到的两个条件都适用于五个连续的完整GC周期
UseGCOverheadLimit
标志未禁用(true为默认值)
运行完整的GC意味着JVM总是耗尽内存。 如果花费了98%的时间来释放仅2%的堆,则意味着CPU几乎完全忙于GC,几乎无法完成任何应用程序逻辑。 这就是为什么放弃并抛出OutOfMemoryError
并显示以下消息的原因:
Exception in thread “main” java.lang.OutOfMemoryError: GC overhead limit exceeded
翻译自: https://www.javacodegeeks.com/2017/08/what-causes-outofmemoryerror.html