目前有三大Java虚拟机:HotSpot,oracle JRockit,IBM J9。
JRockit是oracle发明的,用于其WebLogic服务器,IBM JVM是IBM发明的用于其Websphere服务器(所以在某行开发的时候,他们用的是IBM的JDK,因为他们使用的IBM的应用程序服务器Websphere,使用其他JDK可能存在兼容性问题)。
JRockit和J9不存在永久代这种说法。这里只讨论HotSpot虚拟机,这也是目前使用的最多的JVM。Sun JDK7 HotSpot虚拟机的内存模型如下图所示:
1、什么是方法区
一、方法区与永久代
这两个是非常容易混淆的概念,永久代的对象放在方法区中,就会想当然地认为,方法区就等同于持久代的内存区域。事实上两者是这样的关系:
《Java虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。那么,在不同的 JVM 上方法区的实现肯定是不同的了。 同时大多数用的JVM都是Sun公司的HotSpot。在HotSpot上把GC分代收集扩展至方法区,或者说使用永久代来实现方法区。换句话说:方法区是一种规范,永久代是Hotspot针对这一规范的一种实现。而永久代本身也在迭代中:
在Java 6中,方法区中包含的数据,除了JIT编译生成的代码存放在native memory的CodeCache区域,其他都存放在永久代;
在Java 7中,Symbol的存储从PermGen移动到了native memory,并且把静态变量从instanceKlass末尾(位于PermGen内)移动到了java.lang.Class对象的末尾(位于普通Java heap内);
在Java 8中,永久代被彻底移除,取而代之的是另一块与堆不相连的本地内存——元空间(Metaspace),‑XX:MaxPermSize 参数失去了意义,取而代之的是-XX:MaxMetaspaceSize。
对于Java8, HotSpots取消了永久代,那么是不是也就没有方法区了呢?当然不是,方法区是一个规范,规范没变,它就一直在。那么取代永久代的就是元空间。它与永久代有什么不同的?
存储位置不同,永久代是堆的一部分,和新生代,老年代地址是连续的,而元空间属于本地内存;
存储内容不同,元空间存储类的元信息,静态变量和常量池等并入堆中。相当于永久代的数据被分到了堆和元空间中。
二、方法区里存着什么?
既然永久代是方法区的一种实现,那么在Hotspot下,方法区就等于永久代,也被称为非堆。那方法区里都存着什么呢?先抛结论:
静态变量 + 常量 + 类信息(构造方法/接口定义) + 运行时常量池存在方法区中 。
2、方法区和永久代的关系
在Java虚拟机规范中,方法区在虚拟机启动的时候创建,虽然方法区是堆的逻辑组成部分,但是简单的虚拟机实现可以选择不在方法区实现垃圾回收与压缩。这个版本的虚拟机规范也不限定实现方法区的内存位置和编译代码的管理策略。所以不同的JVM厂商,针对自己的JVM可能有不同的方法区实现方式。
永久代是Hotspot虚拟机特有的概念,是方法区的一种实现,别的JVM都没有这个东西。在Java 8中,永久代被彻底移除,取而代之的是另一块与堆不相连的本地内存——元空间。
永久代或者“Perm Gen”包含了JVM需要的应用元数据,这些元数据描述了在应用里使用的类和方法。注意,永久代不是Java堆内存的一部分。永久代存放JVM运行时使用的类。永久代同样包含了Java SE库的类和方法。永久代的对象在full GC时进行垃圾收集。
在HotSpot中,设计者将方法区纳入GC分代收集。HotSpot虚拟机堆内存被分为新生代和老年代,对堆内存进行分代管理。
方法区和永久代的关系很像Java中接口和类的关系,类实现了接口,而永久代就是HotSpot虚拟机对虚拟机规范中方法区的一种实现方式。
我们知道在HotSpot虚拟机中存在三种垃圾回收现象,minor GC、major GC和full GC。对新生代进行垃圾回收叫做minor GC,对老年代进行垃圾回收叫做major GC,同时对新生代、老年代和永久代进行垃圾回收叫做full GC。许多major GC是由minor GC触发的,所以很难将这两种垃圾回收区分开。major GC和full GC通常是等价的,收集整个GC堆。但因为HotSpot VM发展了这么多年,外界对各种名词的解读已经完全混乱了,当有人说“major GC”的时候一定要问清楚他想要指的是上面的full GC还是major GC。
3、元空间
上面说过,HotSpot虚拟机在1.8之后已经取消了永久代,改为元空间,类的元信息被存储在元空间中。元空间没有使用堆内存,而是与堆不相连的本地内存区域。所以,理论上系统可以使用的内存有多大,元空间就有多大,所以不会出现永久代存在时的内存溢出问题。这项改造也是有必要的,永久代的调优是很困难的,虽然可以设置永久代的大小,但是很难确定一个合适的大小,因为其中的影响因素很多,比如类数量的多少、常量数量的多少等。永久代中的元数据的位置也会随着一次full GC发生移动,比较消耗虚拟机性能。同时,HotSpot虚拟机的每种类型的垃圾回收器都需要特殊处理永久代中的元数据。将元数据从永久代剥离出来,不仅实现了对元空间的无缝管理,还可以简化Full GC以及对以后的并发隔离类元数据等方面进行优化。