JVM(hotspot)为什么使用元空间替换了永久代

一、我们先来理解两个概念:规范和实现

《Java虚拟机规范》

JVM(hotspot)为什么使用元空间替换了永久代_第1张图片

方法区和堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

方法区是一种规范,不同的虚拟机厂商可以基于规范做出不同的实现,永久代和元空间就是出于不同jdk版本的实现

jdk7之前hotspot用永久代(非堆,但是虚拟机中)实现了方法区,jdk8改为了使用元空间(元空间并不在虚拟机中,而是使用本地内存)。字符串常量移至Java Heap

32 位机器默认的永久代的大小为 64M,64 位的机器则为 85M。

metaspace 默认最小20.75M,最大无限。

二、元空间的信息查看

jmap -clstats :打印类加载器的统计信息

jstat -gc:   打印空间使用情况

三、替换原因

1. 避免OOM

    避免OOM异常。因为通常使用PermSize和MaxPermSize设置永久代的大小就决定了永久代的上限,但是不是总能知道应该设置为多大合适, 如果使用默认值很容易遇到OOM错误。Metaspace存在本地内存中,由系统的实际可用空间来控制,当然也可以控制大小 -XX:MaxMetaspaceSize

    由于方法区主要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。最典型的场景就是,在 jsp 页面比较多的情况,容易出现永久代内存溢出java.lang.OutOfMemoryError: PermGen space

2.持久代本身的问题

HotSpot的内部类型也是Java对象:它可能会在Full GC中被移动,同时它对应用不透明,且是非强类型的,难以跟踪调试,还需要存储元数据的元数据信息(meta-metadata)

3.提高GC性能

永久代的对象在full GC时进行垃圾收集,也就是和老年代同时垃圾收集。

替换后,简化了Full GC,可以在GC不进行暂停的情况下并发地释放类数据。

GC性能提升主要表现为:

  • Full GC中,元数据指向元数据的那些指针都不用再扫描了。很多复杂的元数据扫描的代码(尤其是CMS里面的那些)都删除了。
  • 元空间只有少量的指针指向Java堆。这包括:类的元数据中指向java/lang/Class实例的指针;数组类的元数据中,指向java/lang/Class集合的指针。
  • 没有元数据压缩的开销
  • 减少了根对象的扫描(不再扫描虚拟机里面的已加载类的字典以及其它的内部哈希表)
  • 减少了Full GC的时间
  • G1回收器中,并发标记阶段完成后可以进行类的卸载

4.合并HotSpot和JRockit

合并HotSpot和JRockit的代码,JRockit从来没有所谓的永久代,也不需要开发运维人员设置永久代的大小,但是运行良好。同时也不用担心运行性能问题了

5.未来扩展的需要

使得原来受限于持久代的一些改进未来有可能实现

四、元空间介绍

1. 元空间的特点

  • 充分利用了Java语言规范中的好处:类及相关的元数据的生命周期与类加载器的一致。
  • 每个加载器有专门的存储空间
  • 只进行线性分配
  • 不会单独回收某个类
  • 省掉了GC扫描及压缩的时间
  • 元空间里的对象的位置是固定的
  • 如果GC发现某个类加载器不再存活了,会把相关的空间整个回收掉

2. 垃圾回收

对于僵死的类及类加载器的垃圾回收,将在元数据使用达到“MaxMetaspaceSize”参数的设定值时进行。

适时地监控和调整元空间对于减小垃圾回收频率和减少延时是很有必要的。持续的元空间垃圾回收说明,可能存在类、类加载器导致的内存泄漏或是大小设置不合适。

3. 元空间的问题

  元空间虚拟机采用了组块分配的形式,同时区块的大小由类加载器类型决定。类信息并不是固定大小,因此有可能分配的空闲区块和类需要的区块大小不同,这种情况下可能导致碎片存在。元空间虚拟机目前并不支持压缩操作,所以碎片化是目前最大的问题。

你可能感兴趣的:(JDK基础)