方法区与永久代、元空间之间的关系
方法区是JVM规范中定义的一块内存区域,用来存储类元数据、方法字节码、即时编译器需要的信息等
方法区是一种抽象的概念,它是一种虚拟机设计规范。
永久代是Hotspot虚拟机对JVM规范的实现(1.8之前)
元空间是Hotspot虚拟机对JVM规范的实现(1.8以后),使用本地内存作为这些信息的存储空间
因此其实永久代以及元空间其实就是对JVM方法区的一种具体实现
那么为什么JDK1.8之后永久代被元空间代替了呢?
有如下几个原因:
1、整个永久代有一个 JVM 本身设置的固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。当元空间溢出时会得到如下错误: java.lang.OutOfMemoryError: MetaSpace你可以使用
-XX:MaxMetaspaceSize 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。
-XX:MetaspaceSize 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。
2、元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 MaxPermSize 控制了, 而由系统的实际可用空间来控制,这样能加载的类就更多了。
3、在 JDK8,合并 HotSpot 和 JRockit 的代码时, JRockit 从来没有一个叫永久代的东西, 合并之后就没有必要额外的设置这么一个永久代的地方了。
下面是一个类在元空间和堆中加载的流程
上面左侧的箭头按照创建的时间顺序,Heap表示堆,Metaspace表示元空间。
我们想要加载类,首先需要一个类加载器Class Loader。
之后当我们要加载类的时候,例如上面加载的X类,那么此时他就是将X类的元信息加载到了我们的元空间中,然后他也会在堆中创建X.class对象。而X类的原始信息此时就在元空间中了,如果想要访问元信息,那么就需要通过一个Java类去访问,也就是这里的X.class对象。Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
同理类Y也是。
因此在进行类加载的时候,就会把类的原始信息放入到元空间中。
而对于元空间中内存的释放,是没有那么容易的。
他要求我们的类加载器被回收的时候,元空间的类的原始信息才会被回收。
也就是在堆中类X和类Y的对象被回收完毕之后,此时如果没有需要加载其他类了,那么此时类加载器就会被回收,之后元空间的类原始信息也会被回收。
也就是不光光是对象实例不被使用,对象对应的Class类不再被使用了,以及类加载器不再被使用了才能释放元空间中的原始数据。