前言

在前面的文章里,对JVM运行时数据区里面的程序计数器Java虚拟机栈本地方法栈做了比较详细的讲解。接下来,我们来说说方法区

理解JVM运行时数据区(五)方法区

什么是方法区

我们先来看下《Java虚拟机规范》里面对方法区的定义: 《Java虚拟机规范》方法区连接

理解JVM运行时数据区(五)方法区

这里面对方法区进行了定义:

  • 它是一个线程共享的内存区域
  • 用于存储被虚拟机加载的类信息、字段信息线程和进程的区别是什么、方法信息,以及方法和构造函数的代码,包括类和实际初始化以及接口初始化中使用的特殊方法(简单理解就是类的初始化方法<init>)
  • 在虚拟机启动时创建
  • 尽管在逻辑变量名的命名规则上是堆的一部分,但可以选择不进行垃圾回收或压缩它(可以看作是一块独立于Java堆的内存空间)
  • 方法区可以是固定字符型变量大小,也可以根据计算的需要进行扩展
  • 方法区的内存不需要是变量泵连续的
  • 如果方法区中的内存无法满足分配请求,Java虚拟机字符串是什么意思变量是什么意思会抛出OutOfMemoryError

但是,由于《Java虚拟机规范》只规定了概念和作用,对如何实现方法区不做统一要求。因此不同的虚拟机有着不字符间距加宽2磅同的实现,甚至于相同的虚拟机在不同的版本上实现也不相同。比如HotSpot虚拟机,在jdk7之前、jdk7、字符间距在哪里设置jdk8及之后的版本对方法区的实现都不一样。

这边对方法区存储的信息做一个补充,这也是网上大部分文章描述的:方法区里面存储类信息字段信息方法信息常量静态变量即时编译器编译字符型变量后的代码缓存等数据。

这段话如果放在jdk1.7之前没问题,但是放在现在的jdk接口卡版本上,它就有问题了,因为静态变量在jjvm是什么意思dk1.7的时候已经jvm优化被移出方法区,放到了堆中了。因此此时jvm内存模型方法区里字符常量面是没有静态变量了。

在jdk7及之前,习惯上把方法区称为永久代。从jdk8开始,使用元空间取代了永久代线程是什么意思

这边说下在之前为什么会习惯将方法区称为永久代,这是因为在HotSpot虚拟机上用永久代来实现方法区的逻辑(对于JRockit和J9虚拟机来说,是不存在”永久代”的概念的)。而HotSpot虚拟机是全世界使用最广泛的Jajvm内存模型va虚拟机,大部分java程序开发都是使用Hotspot虚拟机,因此在之前会习惯称方法区为永久代。

永久代和元空变量类型有哪些间的演进

永久代和元空间虽jvm面试题然都变量是对方法区字符串是什么意思的落地实现,但是二者不只是名字不同,数据区位置变量英语和内部结构也调整了,下面通过图片来展示永久代到元空间的演进。

jdk6及之前

理解JVM运行时数据区(五)方法区
在jdk6及以前,方法区使用永久代来实现,永久代放在堆空间中。此时里面放着类信息、字段信息、方法信息、常量、运行时常量池、静态变量、即时编jvm调优译器编译后的代码缓存等数据

jdk7

理解JVM运行时数据区(五)方法区
从jdk7开接口自动化始,静态变量和字符串常量池被移出永久代,并放入堆空间中。接口自动化

jdk8

理解JVM运行时数据区(五)方法区
到了jdk8的时候,永久代被彻底废除,采用元空间来实现方法区,此时元空间是放在接口类型本地内存中的。而原先永久字符间距代里面存放的数据也相应的字符存放到了元空间,不过静态变量和字符串常量池依旧放在堆空间中。

为什么会使用元空间替换掉永久代?

  • 由于使用永久代实现方线程池面试题法区的方式在后面发现变量名的命名规则了诸多问题,相比其他的虚拟机更容易出现OOM
    • 永久代调优困难
    • 垃圾回收效果不好
  • 永久代在虚拟机内部的堆空间中,本身大小就受变量名的命名规则到了限制,就算再大也无法突破堆空间的大小限制。
  • 元空间并不在虚拟机中,而是使用本地内存,因此默认情况下元空间的大小线程是什么意思仅受本地内存的限制,虽然仍旧可能存在内存溢出,但是比原来出现的概率会更小。
  • Oracle收购了号称世界最快的JRockit虚拟机字符间距在哪里设置,并整合了JRockit虚拟机的优秀功能。既然JRockit使用线程的是比永久代更好的元空间,那就干脆去掉永久代,使用元空间。

方法区里面存接口和抽象类的区别放数据

上面我们讲到方法区里面存放着接口类信息、字线程和进程的区别是什么接口卡信息、方法信息、运行时常量池以及即时编译器编译后的代码缓存等信息。这边我找了一张图,以便大家能更直观的了解。

理解JVM运行时数据区(五)方法区

类信息

当类被加载的时候,JVM会将被加载的类信息放在方法区里面,这边的类包括以几种:

  • class
  • interface 接口
  • enum 枚举
  • annotation 注解

存储的信息包括以下这线程些:

  • 完整的有效名称,也可以说类的全限线程撕裂者定名
  • 直接父类的全限定名
  • 修饰符(pjvm是什么意思ubl接口crc错误计数ic、变量与函数a字符间距bstract、final的某个子集)
  • 实现的所有接口的信息,这个是放在一个有序列表里面,因为可以实字符间距加宽2磅现多接口。

类加载器的引用

JVM必须知变量之间的关系道一个类是由启动加载器加载的还是由用户类加载器加载的。如果一个类型是由用户类加载器加载的,那么JVM会将这个类加载器的引用字符间距加宽2磅作为类信息的一部分保存在方法区中。JVM在动态连接的时候需要这个信息。当解析一个类到另一个类的引用的时字符间距在哪里设置候,JVM需要保证这两个类的类变量名的命名规则加载器是相同的。这对JVM区分名字空间的方式是至变量名的命名规则关重要的。

Class实例的引用

JVM为每个加载的类都创建一个java.lang.Class的实例(存储在堆上)。而JVM必须以某种方式把Class的这个实例和存储在方法区中的类型数据(类元数据)联系起来。因此,类的元数据里面保存着一个Class接口卡对象的引用

字段信息或称为域信息接口和抽象类的区别

  • 字段声明的顺序
  • 字段名称
  • 线程和进程的区别是什么段类型
  • 字段的修饰符

注意: 域(Field) = 字段 = 属性 = 成员变量 ,这些说的都是一个意思

方法信息

  • 方法名称
  • 方法返回类型
  • 方法参数的数量和类型
  • 方法的修饰符
  • 方法的字节码、操作jvm面试题数栈、局部变量表及大接口自动化小(abstract和native除外)
  • 异常表:每个异常处理的开始位置、jvm调优结束位置、代码处理jvm性能调优在程序计数器中的偏字符常量移地址、被字符间距在哪里设置捕获的异常类和常量池引用。(abstract 和 nativ变量泵e线程池 除外)

方法表

JVM对每个加载的非虚拟类的类信息中都添加了一个方法表,方法表是一组对类线程和进程的区别是什么实例方法的直接引用(包括从父类继承的方法)JVM可以通过方法表快速的激活实例方法。

运行时常量接口自动化

在类加载时,也会将.class的字节码文件中的常量池载入到内存中,并保存在方法区中。我们常说的常量变量与函数池就是只方法接口crc错误计数区中的运行时常量池。下面会简单对运行时常量池做一个描述。

即时编译期编译后的缓存代码(JIT代码缓存)

从字面意思理解就是代码缓存区,它缓存的是JIJVMT(Just in Time)即时变量泵编译期编译的代码。JVM会对频繁使用的代码即热点代码,在达字符串逆序输出到一定的使用次数后,会编译成本地平台相关的机器码,这样在下次执行的时候就能更快的运行。

  • 被多次调用的方法
  • 被多次执行的循环体

什么是运行时常量池

如果要了解运行时常量池,那么应当先了解下什么是常量池

我们先来看下一个简单的字节码文件:(屏幕有限,只能截这么大)

理解JVM运行时数据区(五)方法区

可以看到当字节码文件中有个Constant pool,这个是class文件中的常量池。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有个用于存放编译期生成的各种字面量符号引用的常量池(Constan变量类型有哪些t pool)。

字面量 :

  • 文本字符串
  • 被声明为final的常量值
  • 基本数据类型值 (如果是int值的话 大于-3276字符间距加宽2磅8并小于32767 是不会在常量池里面的,而是跟在字节码指令后面)

符号引用:

  • 类和接口的全限定名 如#9 = Class
  • 字段的名称和描述符 如#7 = Fieldref
  • 方法的名称和描述符 如#1 = Methodref

说完常量池,接下来来说说运行时常量池

简单来说线程撕裂者就是:JVM在完成类装载操作后,字符间距加宽2磅会将class文件中的常量载入到内存中,并保存在方法区。而放在方法区里的这块内存被称为运行时常量池。

实际上这个载入到内存中是很复杂的,这边只简单变量描述:

  • class文件中的常量池内容会在类加载时进入方法区的运行时常量池中(并不是全部完整的放进去)
    • 符号引用会解析成直接引用放在运行时常量池
    • 如果是字符串的话,接口文档会先在堆中创建字符串对象实例,然后将该对象的引用放到字符串常量池中,最后将运行时常量池里的JVM符号引用替换成直接引用
  • java虚拟机为每个类和接口维护一个运行时常量池

关于常量池的描述,感兴趣的可以看这个Java中几种常量池的区分

字符串常量池为啥要放在堆中

jdk7中将字符串常量池放到了堆jvm垃圾回收机制空间,因为永久代的回收效率低,在full gc 的时候才会触发,而full gc 是老年代jvm优化的空间不足、永久代不足时才会触发。这就导致了字符串常量池回收效率不高。而我们开发中会有大量的字符串被创建,回收效率低就会导致永久代内存不足。放到堆里能及时回收内存。

结束语

关于运行数据区的方法区就介绍接口卡到这里,感谢大家的阅读。文中如果有写的不好的地方,请在评论区指出线程是什么意思,再次感谢。

系列文章

  • 理解JVMjvm是什么运行时数据区(一)概念
  • 理解JVM运行时数据区(二)程序计字符间距数器
  • 理解JVM运行时数据区(三)Java虚拟机栈
  • 理解JVM运行时数据区(四)本地方法栈
  • 理解JVM运行时数据区(五)方法区
  • 理解JVM接口运行时数据区(六)堆
  • 变量值解JVM运行时数据区(七)垃圾回收