性能文章>Java OOM 基础篇:常见的OutOfMemoryError 场景四: Permgen size 元空间问题详解>

Java OOM 基础篇:常见的OutOfMemoryError 场景四: Permgen size 元空间问题详解转载

2年前
7165321

Java OOM优化专题大纲目录

导语

此文来自于plumbr官网,plumbr作为一个常用的JVM 监测工具,官网有完整的oom和gc文章,准备慢慢全部翻译过来:

Java OOM 基础篇:常见的OutOfMemoryError 场景一:Java heap space 堆溢出问题详解

Java OOM 基础篇:常见的OutOfMemoryError 场景二 : GC overhead limit exceeded 问题详解

Java OOM 基础篇:常见的OutOfMemoryError 场景三: PermGen space 永久空间问题详解

本篇详细的介绍了java下Permgen size  元空间问题以及如何解决这个问题,希望能对大家有所帮助。

 

正文:

Java 对程序可以分配的最大数组大小有限制。确切的限制是特定于平台的,但通常在 1 到 21 亿个元素之间。

内存不足错误

当您遇到java.lang.OutOfMemoryError: Requested array size exceeded VM limit 时,这意味着因错误而崩溃的应用程序正在尝试分配大于 Java 虚拟机可以支持的数组。

1,造成的原因是什么?

该错误是由 JVM 中的本机代码引发的。当 JVM 执行特定于平台的检查时,它发生在为数组分配内存之前:分配的数据结构在此平台中是否可寻址。此错误并不像您最初想象的那么常见。

您很少遇到此错误的原因是 Java 数组由 int 索引。Java 中最大的正整数是 2^31 – 1 = 2,147,483,647。特定于平台的限制可能非常接近这个数字——例如,在我的 Java 1.7 上的 64 位 MB Pro 上,我可以愉快地初始化最多 2,147,483,645 或Integer.MAX_VALUE-2元素的数组。

将数组的长度增加 1 到 Integer.MAX_VALUE-1 会导致熟悉的OutOfMemoryError

Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit

但是限制可能不会那么高——在带有 OpenJDK 6 的 32 位 Linux 上,当分配一个包含约 11 亿个元素的数组时,你会遇到“ java.lang.OutOfMemoryError:请求的数组大小超过 VM 限制”。要了解特定环境的限制,请运行下一章中描述的小测试程序。

2,举个例子

尝试重新创建java.lang.OutOfMemoryError: Requested array size exceeded VM limit错误时,我们看下面的代码:

for (int i = 3; i >= 0; i--) {
	try {
		int[] arr = new int[Integer.MAX_VALUE-i];
		System.out.format("Successfully initialized an array with %,d elements.\n", Integer.MAX_VALUE-i);
	} catch (Throwable t) {
		t.printStackTrace();
	}
}

该示例迭代四次并在每一轮初始化一个长基元数组。此程序尝试初始化的数组大小随着每次迭代增加 1,最终达到 Integer.MAX_VALUE。现在,在使用 Hotspot 7 的 64 位 Mac OS X 上启动代码片段时,您应该获得类似于以下内容的输出:

java.lang.OutOfMemoryError: Java heap space
	at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)
java.lang.OutOfMemoryError: Java heap space
	at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
	at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
	at eu.plumbr.demo.ArraySize.main(ArraySize.java:8

请注意,在面对java.lang.OutOfMemoryError: 请求的数组大小在最后两次尝试中超出 VM 限制之前,分配失败并出现了很多更熟悉的java.lang.OutOfMemoryError: Java heap space消息。发生这种情况是因为您试图为 2^31-1 int 原语腾出空间需要 8G 内存,这小于 JVM 使用的默认值。

这个例子也说明了为什么这个错误如此罕见——为了看到 VM 对数组大小的限制被命中,你需要分配一个大小正好在平台限制和 Integer.MAX_INT 之间的数组。当我们的示例在带有 Hotspot 7 的 64 位 Mac OS X 上运行时,只有两个这样的数组长度:Integer.MAX_INT-1 和 Integer.MAX_INT。

3,解决办法是什么?

所述java.lang.OutOfMemoryError:请求阵列大小超过限制VM可以表现为任一下列情况的结果:

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

在第一种情况下,检查您的代码库,看看您是否真的需要那么大的数组。也许你可以减少数组的大小并完成它。或者将阵列分成更小的块,并批量加载您需要处理的数据以适应您的平台限制。

在第二种情况下——记住 Java 数组是由 int 索引的。因此,在平台内使用标准数据结构时,数组中的元素不能超过 2^31-1。实际上,在这种情况下,您已经被编译器在编译期间宣布“错误:整数太大”所阻止。

但是,如果您真的使用真正的大型数据集,则需要重新考虑您的选择。您可以小批量加载您需要处理的数据,并且仍然使用标准 Java 工具,或者您可以超越标准实用程序。实现此目的的一种方法是查看sun.misc.Unsafe类。这允许您像在 C 中一样直接分配内存。

点赞收藏
金色梦想

终身学习。

请先登录,查看3条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步
21
3