运行时数据区-方法区
方法区是虚拟机规范定义的运行时数据区域之一,是所有线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。《Java 虚拟机规范》把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的是与 Java 堆 区分开来
因为是JVM规范定义,所以不同的JVM厂商,针对自己的JVM可能有不同的方法区实现方式
比如:永久代
是 HotSpot 虚拟机在 JDK7 及以前对方法区的具体实现,而永久代也在堆中
只有HotSpot 虚拟机有永久代,其他虚拟机没有永久代这个概念
元空间
是HotSpot在JDK8 及以后的方法区实现,元空间不再与堆连续,而且是存在于本地内存(Native memory),并且原来类的静态变量和字符串常量池从 JDK1.7开始都被转移到了java堆区,只有class元数据才在元空间。
问题来了?为什么舍弃了永久代?
oracle收购了JRockit虚拟机所在的公司,并且 想融合 Hotspot 和 JRockit
官方回答: 这是JRockit和Hotspot聚合工作的一部分,JRockit客户不需要永久代(因为JRockit没有永久代),并且习惯于不配置永久代
- 永久代的垃圾收集是和老年代捆绑在一起的,因此无论谁满了,都会触发永久代和老年代的垃圾收集,并且永久代回收效率低
- 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出
- 字符串存在永久代中,容易出现性能问题和内存溢出
本文就以原空间为本文方法区的实现,展开一篇文章。
特点
- 类及相关的元数据的生命周期与类加载器的一致
- 方法区与 Java 堆一样,是所有线程共享的内存区域
- 方法区用于存放已被虚拟机加载的类信息、常量、静态变量,即编译器编译后的代码
- 方法区在jvm启动的时候被创建,并且它的实际的物理内存空间中和java堆区一样都是可以不连续的
- 当JVM加载的类信息容量超过了这个值,会报 OutOfMemoryError:Metaspace,可以通过
-XX:MaxPermSize
来设定元空间最大可用空间 - 每个类加载器有专门的存储空间,如果GC发现某个类加载器不再存活了,会把相关的空间整个回收掉
- 垃圾收集并不会单独回收某个类,省掉了GC扫描及压缩的时间
- 元空间里的对象的位置是固定的
使用-XX:MetaspaceSize=256m
和 -XX:MaxMetaspaceSize=256m
可以调整方法区的初始化大小和最大可用空间