JDK8起,Hotspot JVM 废弃永久代(PermGen),同时将类元数据存储在本地内存,称之为元空间(Metaspace),而字符串常量池以及类的静态变量则存储在堆区。

为什么要废弃永久代

  • 新类元数据的分配将受可用本机内存量的限制,而不是固定的由-XX:MaxPermSize参数指定的值,无论是默认值还是在命令行中指定的值。
  • 简化 Full GC 的前置(JEP 156)
  • 合并JRockit的一部分,JRockit没有永久代

注意: 将字符串常量池和类静态变量移至Java堆可能会导致内存不足异常或GC数量增加。用户可能需要对-Xmx进行一些调整。

什么是元空间(metaspace)

元空间是VM用于存储类的元数据的一块非堆空间的本地内存

什么时候分配内存(allocate)

加载类的时候由类加载器分配

什么时候释放内存(release)

卸载类加载器时释放内存,即发生在以下步骤之后才会释放

  • 所有该类加载器加载的所有类没有存活的实例
  • 没有引用指向该类加载器及其加载的类
  • 该类加载器及其加载的类正常GC

注意: “释放内存”不代表将内存返回给OS,该内存全部或一部分继续保留在JVM中,可以将其重用于将来的类加载。不完全释放内存给OS就会造成碎片的产生,所以使用Metaspace会造成碎片,导致内存浪费

相关VM参数

  • -XX:MetaspaceSize=${limit}:触发GC的阈值
  • -XX:MaxMetaspaceSize=${limit}:Metaspace的最大值,超过则OOM(默认受本机内存大小限制)
  • -XX:+UseCompressedOops:开启压缩对象指针(oops:ordinary object pointers)
  • -XX:+UseCompressedClassPointers:开启压缩类指针(默认打开)
  • -XX:CompressedClassSpaceSize:压缩类空间的大小,超过则OOM(仅当开启压缩类指针时生效、默认1G)

参考资料