方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
使用HotSpot虚拟机的用户,更愿意把方法区称为“永久代”,本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集至方法区,或者说用永久代来实现方法区而已。这样HotSpot的垃圾收集器可以像管理Java堆一样管理这部分内存,能省去专门为方法区编写内存管理代码的工作。
移除永久代的工作从JDK1.7就开始了。JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。但永久代仍存在于JDK1.7中,并没完全移除,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。
元空间本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此默认情况下元空间的大小仅受本地内存限制。
即方法区里存放着类的版本、字段、方法、接口和常量池(存储字面量和符号引用)。
符号引用包括:1、类的权限定名;2、字段名和属性;3、方法名和属性。
1、类型信息:
类的完整名称
类的直接父类的完整名称
类的直接实现接口的有序列表
类型标志(类类型还是接口类型)
类的修饰符(public private defautl abstract final static)
2、类型的常量池
存放该类型所用到的常量的有序集合,包括直接常量(字符串、整数、浮点数)和对其他类型、字段、方法的符号引用。
3、字段信息(该类声明的所有字段)
字段修饰符(public、peotect、private、default)
字段的类型
字段名称
4、方法信息
方法信息中包含类的所有方法。
方法修饰符
方法返回类型
方法名
方法参数个数、类型、顺序等
方法字节码
操作数栈和该方法在栈帧中的局部变量区大小
异常表
5、类变量(静态变量)
6、指向类加载器的引用
7、指向Class实例的引用
8、方法表
9、运行时常量池(Runtime Constant Pool)
总结:
HotSpot JVM中,永久代中用于存放类和方法的元数据以及常量池。每当一个类初次被加载的时候,它的元数据都会被放到永久代中。
永久代大小有限制,如果加载的类太多,很可能导致永久代内存溢出,即java.lang.OutOfMemoryError: PermGen。
Java 8中PermGen被移出HotSpot JVM了:
- 由于 PermGen 内存经常会溢出,引发恼人的 java.lang.OutOfMemoryError: PermGen,因此 JVM 的开发者希望这一块内存可以更灵活地被管理,不要再经常出现这样的 OOM
- 移除 PermGen 可以促进 HotSpot JVM 与 JRockit VM 的融合,因为 JRockit 没有永久代。
PermGen最终被移出,方法区移至Metaspace,字符串常量移至Java Heap。
JDK8把类的元数据放到本地堆内存(native heap)中,这一块区域就叫Metaspace,中文名叫元空间。
如果Metaspace的空间占用达到了设定的最大值,那么就会触发GC来收集死亡对象和类的加载器。