方法区是各个线程共享的内存区域,它用于存储 已被虚拟机加载的 类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。 它还有一个别名叫作“非堆”(Non-Heap),目的是与Java堆区分开来。
- 方法区是一个规范,永久代、元空间是它的具体实现
- jdk1.8以前,方法区的实现叫永久代,是存储在堆上的。字符串常量池、静态变量、类型信息等是存储在永久代的
- jdk1.8及以后,方法区的实现叫元空间,是存储在本地内存上的。jdk1.7时,常量池、静态变量被移动到了堆内存,到了jdk1.8,永久代彻底不存在了,变成了元空间
jdk1.8的元空间在本地内存
Java 8以后,关于元空间的JVM参数有两个:-XX:MetaspaceSize
=N和-XX:MaxMetaspaceSize
=N,对于64位的JVM来说,元空间的默认初始大小是20.75MB,默认的元空间的最大值是无限的。MaxMetaspaceSize用于设置Metaspace区域的的最大值,这个值可以通过mxbean中的MemoryPoolBean获取,如果这个参数没有设置,那么就通过maxbean拿到的最大值是-1,表示无穷大。
由于调整元空间的大小需要Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量的Full GC,通常都是由于永久代或元空间发生了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,对应8G物理内存的机器来说,一般将这个值设置成256M。
方法区具体存储的东西:
- 类型元数据
- 类型信息
- 类的完整名称
- 类的直接父类的完整名称
- 类的直接实现接口的有序列表
- 类型标志(类类型/接口类型)
- 类的修饰符
- 类型常量池:存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到运行时常量池中
- 字面量:值
- 符号引用:类的全限定类名、字段名称和描述符、方法名称和描述符
- 字段信息
- 字段名称
- 字段修饰符
- 字段类型
- 方法信息:包含类的所有方法的信息
- 方法名称
- 方法修饰符
- 方法返回类型
- 方法的参数列表信息
- 方法字节码
- 操作数栈和该方法在栈帧中的局部变量区的大小
- 类加载器的引用
- Class实例的引用
- 类型信息
- 运行时常量池:类加载后会把编译期生成的各种字面量和符号引用存放到运行时常量池中;运行时产生的常量也会被放到这里
- 字符串常量池(它只存储对java.lang.String实例的引用,而不存储String对象的内容)
- 基本数据类型的包装类型的常量池(浮点型的包装类型没有常量池)(Byte,Short,Integer,Long:[-128, 127];Character:[0, 127];Boolean:[true, false])
- 静态变量
- 方法表