持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情
JVM之方法区
Java虚拟机内存区域模型
黄色区域: 所有线程共享的内存区域,会存在垃圾回收。
灰色区域: 线程私有不会产生垃圾回收。
方法区是运行时数据区中的一部分,那么方法区具体的用途是什么呢?方法区能存放什么信息呢?
方法区是什么
方法区和堆一样是被所有的线程所共享,有的地方将方法区称之为Non-Heap(非堆),其目的就是将方法区与堆内存区分开来,在JDK1.7中很多开发者将方法区称之为永久代,但本质上两者并不是等价的,仅仅是因为HotSpot虚拟机的设计团队将GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已,这样HotSpot的垃圾收集器可以像管理堆内存一样管理永久代内存了,其余非HotSpot的虚拟机是没有永久代这个概念的。
HotSpot方法区演进
方法区在jdk7及以前,习惯将永久代称之为方法区,结构如下所示
在jdk1.8中采用了Metaspace(元数据空间)替换了Perm(永久区),并且从堆空间分离出来,结构如下所示
一定一定需要注意的是Metaspace(元数据空间)不再存储到堆中,也不使用JVM内存空间而是采用本机的内存,这样元数据只受本地内存大小限制,OOM的问题就不存在了。
为什么JDK1.8需要将方法区从JVM永久代(非直接内存)移到元空间(直接内存)呢?
- JVM永久代存在容量限制,那么类信息存储的容量有限制容易触发OOM,而采用元空间存储,只受本地内存大小限制OOM的可能性小。
- 因为是直接内存,JVM将会在IO操作上具有更高的性能,因为它直接作用于本地操作系统的IO操作,而堆内存做IO操作,先需要将数据复制到直接内存,再利用IO处理,而直接内存不需要这层转换。
堆栈和方法区的交互
如我们常常构建的业务对象,以Car对象为例
当我们在代码中创建一个Car实例对象后,将会在堆、栈、方法区内存空间分别写入数据
- Car:是对象模板,为Class对象,存放在方法区中。
- car:属于引用数据,方法栈空间中。
- new Car():需要在堆中开辟一片空间,存放在堆中。
方法区存放什么数据
方法区存放的数据主要是被类加载器加载后的类信息,运行时常量池等等。
其中有两个点需要注意
- 静态变量,在JDK1.8以前静态变量都是存在永久代内存中,JDK1.8后采用元空间替代永久代,就将静态变量从永久代转移到了堆空间。
- 常量池,在JDK1.7之前运行时常量池逻辑包含了字符串常量池存放在永久代中,JDK1.7将字符串常量池转移到了堆内存中,注意这时并没有提到运行时常量池,也就是说运行时常量池还存在永久代中,JDK1.8将永久代移除用元空间代替,这时运行时常量池就存在元空间中了。
那么类信息又包括什么东西呢?
-
类信息
- 类的完整名字
- 类的直接父类完整名字
- 类的直接实现接口有序列表
- 类型标识(是类还是接口)
- 类的访问修饰符(public、private等等)
-
类型常量池
- 存放该类型用到的常量有序集合,包括直接常量(字符串、浮点、整型),和对其它类型、字段、方法的符号引用。
-
字段信息
- 字段类型、名称、访问修饰符
-
方法信息
- 类的所有方法
- 方法返回类型、方法访问修饰符、方法名称等等
-
类变量
-
指向类加载器的引用
-
指向Class实例的引用
-
方法表