java.lang.OutOfMemoryError: Metaspace

/ Java / 没有评论 / 1430浏览

java.lang.OutOfMemoryError: Metaspace

我发现当我认认真真写技术文章的时候,看的人并不多。当我写热点事件,或者科技新闻后发现阅读量又大幅的提升。于是,我就得出一个结论:技术越深的文章看的人越深;技术中等的次之;技术偏初级的看的人最多!这是为什么呢?原因很简单。这就和买房子一样啊,普通商品房买的人最多,复式套房要少一点,别墅再少一点。写的越深的文章,专家的文章就相当于别墅啊!你说是不是?

好了,言归正传,我们今天来说说 java.lang.OutOfMemoryError: Metaspace 错误。

Metaspace 这个东西是在 JDK8 后才有的。Java 8 彻底将永久代 (PermGen) 移除出了 HotSpot JVM,将其原有的数据迁移至 Java Heap 或 Metaspace。

PermGen 永久代中用于存放类和方法的元数据以及常量池,比如 Class 和 Method 等。永久代是有大小限制的,因此如果加载的类太多,很有可能导致永久代内存溢出,即万恶的 java.lang.OutOfMemoryError: PermGen,为此我们不得不对虚拟机做调优。

所以 java 8 中 PermGen 最终被移除,方法区移至 Metaspace,字符串常量移至 Java Heap。

java8 中堆内存划分

Metaspace 的大小值, 由 -Xmx 和 -XX:MaxMetaspaceSize 等 JVM 启动参数指定. 如果没有明确指定, 则根据平台类型(OS 版本 + JVM 版本)和物理内存的大小来确定。

java.lang.OutOfMemoryError: Metaspace 错误所表达的信息就是元数据区(Metaspace) 已被用满。

由于方法区被移至 Metaspace,所以 Metaspace 的使用量与 JVM 加载到内存中的 class 数量/大小有关。可以明确的说 java.lang.OutOfMemoryError: Metaspace 错误的主要原因, 是加载到内存中的 class 数量太多或者体积太大。

下面我们再使用 javassist 工具加载一些 class 到 Metaspace 中,演示一个 java.lang.OutOfMemoryError: Metaspace 错误!

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("com.xttblog.demo.Generated" + i).toClass();
        }
    }
}

执行这段代码, 随着生成的 class 越来越多, 最后将会占满 Metaspace 空间, 抛出 java.lang.OutOfMemoryError: Metaspace. 在 Mac OS X上, Java 1.8.0_05 环境下, 如果设置了启动参数 -XX:MaxMetaspaceSize=64m, 大约加载 70000 个 class 后 JVM 就会挂掉。

如果抛出与 Metaspace 有关的 OutOfMemoryError,第一解决方案是增加 Metaspace 的大小。

-XX:MaxMetaspaceSize=512m

有一种看起来很简单的方案,是直接去掉 Metaspace 的大小限制。但需要注意,不限制 Metaspace 内存的大小,假若物理内存不足,有可能会引起内存交换(swapping),严重拖累系统性能。 此外,还可能造成 native 内存分配失败等问题。

除此之外,VisualVM、jstat、jstack 都可以监测 Metaspace 的动态。

与 Metaspace 相关的还有 4 新增的 JVM 参数:

参考资料