虚拟机 永久代和元空间

在 JDK 1.7 及以往的 JDK 版本中,Java 类信息、常量池、静态变量都存储在 Perm(永久代)里。类的元数据和静态变量在类加载的时候分配到 Perm,当类被卸载的时候垃圾收集器从 Perm 处理掉类的元数据和静态变量。当然常量池的东西也会在 Perm 垃圾收集的时候进行处理。

JDK 1.8 的对 JVM 架构的改造将类元数据放到本地内存中,另外,将常量池和静态变量放到 Java 堆里。HotSopt VM 将会为类的元数据明确分配和释放本地内存。在这种架构下,类元信息就突破了原来 -XX:MaxPermSize 的限制,现在可以使用更多的本地内存。这样就从一定程度上解决了原来在运行时生成大量类的造成经常 Full GC 问题,如运行时使用反射、代理等。

分配本地内存发给类元数据在一个称为「块」的数据结构上,不同类元数据将会用不同大小的块存放。每个块与一个类加载器相关联,并且所有被这个类加载器加载的类元数据在这个块下分配的空间存储元数据。不同的应用块的大小将会不同,块大小的分配将会被内部碎片和外部碎片所限制。当类加载器不再使用,所有和这个类加载器相关联的块空间将会被释放。

如果服务器内存足够,升级到 JDK 1.8 修改 JVM 参数最简单的办法就是将 -XX:PermSize 和 -XX:MaxPermSize 参数替换为 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize,因为现在里面只存元数据信息了,给它个大的空间肯定没问题。不过堆得空间因为现在要多存储原来在永久代的常量池和静态变量可能需要稍微扩大些。具体可以根据 GC 日志,和 $JAVA/bin 下的工具做一些权衡和调优

总结

  通过上面分析,大家应该大致了解了 JVM 的内存划分,也清楚了 JDK 8 中永久代向元空间的转换。不过大家应该都有一个疑问,就是为什么要做这个转换?所以,最后给大家总结以下几点原因:

  1、字符串存在永久代中,容易出现性能问题和内存溢出。

  2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。

  3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。

  4、Oracle 可能会将HotSpot 与 JRockit 合二为一。

参考文章

Java8内存模型—永久代(PermGen)和元空间(Metaspace) - liuxiaopeng - 博客园

jdk源码剖析五:JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)

上一篇:程序员这样准备面试,提高拿offer的机会


下一篇:pyhon&QT编译