Java 8中的MetaSpace有什么用?

65

我知道在Java 8中,他们用MetaSpace替换了PermGen。但我有几个问题:

  1. 默认情况下,MetaSpace是否是被GC收集的?
  2. 即使通过添加像-XX:+CMSClassUnloadingEnabled这样的参数来GC收集PermGen,那么什么使MetaSpace比PermGen更好?
  3. MetaSpace基于本地内存,因此它将Java对象保存在磁盘上而不是VM上?
  4. 即使MetaSpace也可能耗尽内存吗?如果是这样,我将再次收到OutOfMemoryException
  5. 默认情况下,随着内存的增加,MetaSpace是否可以扩大?
3个回答

60

默认情况下,MetaSpace会进行垃圾回收吗?

是的,当MetaSpace被占满时,垃圾回收将运行于它上面,同时也会动态增加(如果允许)为元数据分配的内存。

即使像添加了-XX:+CMSClassUnloadingEnabled这样的参数以对PermGen进行垃圾回收,那么MetaSpace与PermGen相比有何优势?

改进在于metaspace的动态扩展,这是permgen无法实现的。

MetaSpace基于本地内存,所以它将Java对象保存在磁盘而不是VM上吗?

根据metaspace的描述,它仅使用本地内存(没有分页)。

根据Pierre-Hugues Charbonneau的研究(链接在此),引入metaspace并不能解决OOM问题,最多只是给问题贴上一个临时的“创可贴”,它试图动态调整metaspace内存以适应加载的类数量的增长,可能会带来不受控制的增长(只要本地内存允许)。

我们可以通过设置JVM的MaxMetaspaceSize参数并运行提供的示例程序来实现著名的OOM错误。

特别感谢Pierre-Hugues Charbonneau。


2
是的,在metaspace空间占用过高时,GC会运行,并且它还可以动态地增加(如果允许)为元数据分配的内存。 GC会运行时堆空间不足吗? - gstackoverflow
拥有垃圾收集的想法是为了防止这种情况发生。但是它可能会接近满载,然后启动垃圾收集。但是通过智能内存配置和合理编写代码可以避免这种情况。此外,拥有大量 RAM 可以延长不可避免的情况 :) - Anantha Sharma
改进在于元空间的动态扩展,这是永久代无法实现的。你是说永久代无法扩展而元空间可以吗?它们都可以通过提供参数来限制。那么为什么元空间可以扩展呢? - ernesto
@ernesto 如果您不指定元空间的上限,它将会根据需要(和可能性)进行扩展。 - Jeffiekins
从我自己的观察来看,除非达到限制,否则它似乎不会被垃圾回收,这使得为长时间运行的进程设置合理的限制成为必要。 - theglauber
元空间现在已经超出了堆空间。垃圾回收如何在元空间上运行?请解释。 - Deepak

16

回答:

  1. 默认情况下,如果Metaspace内存达到MaxMetaspaceSize,则会进行收集。该参数最初为无限制。限制取决于计算机中的内存。但是当类和类加载器不再需要时,内存会自动释放。只有在怀疑ClassLoader存在内存泄漏时才需要调整此参数。

  2. MetaSpace使用本地内存,指针在内存中的组织使得GC比更老的PermGen内存更快。

  3. 不,这意味着JVM像普通的C程序一样使用内存,并且不使用虚拟内存空间来存储Java对象。看起来内存仅受机器限制。请注意,如果需要,计算机的内存可以交换到磁盘上。

  4. 如果设置了参数MaxMetaspaceSize,则可能会发生OutOfMemory错误;如果未设置此参数,则可能会发生进程分配所有机器内存(包括交换空间)的情况。


如果达到了MetaspaceSize而不是MaxMetaspaceSize,则返回已转换的文本。 - jmj
Metaspace绝对不会进入交换空间(在Linux 64位上测试Java 8u60)。如果出现泄漏,它可能会将其他所有内容推到交换空间,使机器停止运行。 - kubanczyk
3
@JigarJoshi 我刚刚测试了一下,默认情况下,如果达到MaxMetaspaceSize而不是MetaspaceSize,那么将会回收Metaspace。 - kubanczyk
@kubanczyk,你错了。Java元空间可以被交换。显然,如果它被广泛使用,则其他应用程序的内存将被交换。 "直接内存"可能会被误解为其含义取决于上下文。在这里,它的意思是“不是Java虚拟内存,而是操作系统内存”。但是,“操作系统虚拟内存”是分配在RAM和SWAP中的。在Linux中,只有特权用户才能直接分配RAM中的内存(请参见kmalloc)。JAVA是一个用户进程(我认为它使用mmap)。 - mcoolive
@mcoolive 实际上,我的 Linux 服务器曾多次因某些 Metaspace(即类/加载器)泄漏而崩溃。在 Linux 中,用户空间程序可以固定虚拟内存,使其始终驻留在 RAM 中。我的 16GB 类文件并未被广泛使用,并且是由非 root 用户分配的,但没有一个字节被交换出去。在压力下,操作系统的关键部分被交换出去,导致了停机。 - kubanczyk
@kubanczyk,当您说我们应该为元空间定义一个最大大小时,我完全同意您的观点。如果没有指定最大值,在出现泄漏时非常危险。而现在对整个操作系统都非常危险...但是,您的说法“[内存]并没有广泛使用,但是没有一个字节被交换出去”,让我非常惊讶。在您的情况下,可能是GC一直在运行(在标记阶段期间,GC扫描所有可达对象,因此连续GC可以将页面“固定”在RAM中...) - mcoolive

6
  1. 默认情况下,MetaSpace会被垃圾回收吗?

    当类元数据的使用量达到“MaxMetaspaceSize”时,死亡类和类加载器的垃圾收集会触发。默认值为"unlimited",因此需要适当监控以限制这种GC的延迟或频率。

  2. 即使通过添加参数如-XX:+CMSClassUnloadingEnabled来进行PermGen的GC收集,那么MetaSpace比PermGen更好的是什么?

    主要目标是删除permgen,以便用户不必考虑正确调整其大小。

  3. MetaSpace基于本地内存,所以它将Java对象保存在磁盘上而不是VM上吗?

    磁盘不是本地内存,而是存储设备。在此上下文中,本地内存是留给Java堆的进程的内存区域。

  4. 即使MetaSpace也可能耗尽内存吗?

    是的,它受限于您机器上的内存量。


10
哈哈,第二点让我笑了。我的一台生产服务器今天已经崩溃了两次,因为Metaspace使用了所有的RAM并将其他所有内容都推到了Swap。这让我想到如何调整Metaspace大小:D。 - kubanczyk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接