Java应用程序只允许使用有限数量的内存。特定应用程序可以使用的确切内存量是在应用程序启动期间指定的。为了使事情更复杂,Java内存被分成不同的区域,如下图所示:
所有这些区域(包括元空间区域)的大小可以在JVM启动期间指定。如果您自己不确定大小,将使用特定于平台的默认值。
这个java.lang.OutOfMemoryError:Metaspace消息表示内存中的元空间区域已用尽。
是什么引起的OutOfMemoryError?
如果您不是Java领域的新手,那么您可能熟悉Java内存管理中的另一个概念PermGen。从Java8开始,Java中的内存模型发生了显著的变化。引入了一个称为元空间的新内存区域,并删除了Permgen。这一变化是由于各种原因造成的,包括但不限于:
- 所需的permgen的大小很难预测。这导致了要么是拨备不足触发java.lang.OutOfMemoryError:Permgen大小错误或过度配置导致资源浪费。
- GC性能改进,支持并行类数据的取消分配,而无需GC暂停和元数据上的特定迭代器
- 支持进一步的优化,比如G1并发类卸载。
Java8:从永久代PermGen到元空间Metaspace:https://javakk.com/421.html
因此,如果您熟悉PermGen,那么作为背景,您只需要知道-java8之前PermGen中的内容(类的名称和字段、带有方法字节码的类的方法、常量池、JIT优化等)现在都位于元空间中。
如您所见,元空间大小要求既取决于装入的类的数量,也取决于此类声明的大小。所以很容易看出java.lang.OutOfMemoryError:Metaspace是:加载到Metaspace的类太多或太大。
给我举个例子
正如我们在上一章中所解释的,元空间的使用与加载到JVM中的类的数量密切相关。以下代码是最直接的示例:
public class Metaspace {
static javassist.ClassPool cp = javassist.ClassPool.getDefault();
public static void main(String[] args) throws Exception{
for (int i = 0; ; i++) {
Class c = cp.makeClass("eu.plumbr.demo.Generated" + i).toClass();
}
}
}
在本例中,源代码迭代循环并在运行时生成类。所有这些生成的类定义最终都会消耗元空间。类生成的复杂性由javassist库处理。
在生成新的代码之前,元空间和加载空间将被完全利用java.lang.OutOfMemoryError:抛出Metaspace。当以-XX:MaxMetaspaceSize=64m
启动时,在Mac OS X上,我的Java 1.8.0_05会在大约70000个类的加载下消亡。
解决办法是什么?
当面对元空间导致的OutOfMemoryError时,第一个解决方案应该很明显。如果应用程序耗尽内存中的元空间区域,则应增加元空间的大小。更改应用程序启动配置并增加以下内容:
-XX: MaxMetaspaceSize=512m
上面的配置示例告诉JVM在开始以OutOfMemoryError的形式抱怨之前,允许Metaspace增长到512MB。
另一种解决方案乍一看甚至更简单。通过删除此参数,可以完全取消对元空间大小的限制。但是请注意这样做可能会引入大量交换或到达本机分配失败。
不过,在结束这一晚之前,请注意——通常情况下,通过使用上述推荐的“快速修复”,您最终会通过隐藏java.lang.OutOfMemoryError:Metaspace
,而不解决根本问题。如果您的应用程序泄漏内存或只是将一些不合理的东西加载到Metaspace中,上述解决方案实际上不会改善任何东西,它只会推迟问题的发生。
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/433.html
暂无评论