Descripción del área de método del área de datos de tiempo de ejecución de JVM (5)

prefacio

En el artículo anterior, se explicaron en detalle el contador de programa , la pila de máquina virtual de Java y la pila de métodos nativos en el área de datos de tiempo de ejecución de JVM . A continuación, hablemos del área de método .

imagen.png

¿Qué es un área de método?

Veamos primero la definición del área de método en la "Especificación de máquina virtual Java": conexión del área de método "Especificación de máquina virtual Java"

imagen.png

El área del método se define aquí:

  • Es un área de memoria compartida por hilos.
  • Se utiliza para almacenar la información de la clase, la información del campo, la información del método y el código de método y constructor cargado por la máquina virtual, incluida la inicialización real y de la clase y los métodos especiales utilizados en la inicialización de la interfaz (la comprensión simple es el método de inicialización de la clase <init>)
  • Creado en el inicio de la máquina virtual
  • Aunque es lógicamente parte del montón, se puede elegir que no se recolecte basura ni se compacte ( se puede considerar como un bloque de espacio de memoria independiente del montón de Java )
  • El área del método puede ser de tamaño fijo o ampliable según las necesidades del cálculo
  • La memoria del área de método no necesita ser contigua
  • Si la memoria en el área del método no puede satisfacer la solicitud de asignación, la máquina virtual Java arrojará OutOfMemoryError

Sin embargo, dado que la "Especificación de la máquina virtual de Java" solo especifica el concepto y la función, no existe un requisito unificado sobre cómo implementar el área del método. Por lo tanto, diferentes máquinas virtuales tienen diferentes implementaciones, e incluso la misma máquina virtual tiene diferentes implementaciones en diferentes versiones. Por ejemplo, la máquina virtual HotSpot tiene diferentes implementaciones del área de métodos antes de jdk7, jdk7, jdk8 y versiones posteriores.

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

这段话如果放在jdk1.7之前没问题,但是放在现在的jdk版本上,它就有问题了,因为静态变量在jdk1.7的时候已经被移出方法区,放到了堆中了。因此此时方法区里面是没有静态变量了。

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

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

永久代和元空间的演进

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

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

jdk7 imagen.png 从jdk7开始,静态变量和字符串常量池被移出永久代,并放入堆空间中。

jdk8 1738021e6a5d7e62_tplv-t2oaga2asx-zoom-in-crop-mark_1304_0_0_0.awebp 到了jdk8的时候,永久代被彻底废除,采用元空间来实现方法区,此时元空间是放在本地内存中的。而原先永久代里面存放的数据也相应的存放到了元空间,不过静态变量和字符串常量池依旧放在堆空间中。

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

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

方法区里面存放数据

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

4fe9783d873c4fb69e61e55e97a30b85_tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.awebp

类信息

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

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

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

  • 完整的有效名称,也可以说类的全限定名
  • 直接父类的全限定名
  • 修饰符(public、abstract、final的某个子集)
  • 实现的所有接口的信息,这个是放在一个有序列表里面,因为可以实现多接口。

类加载器的引用

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

Class实例的引用

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

字段信息或称为域信息

  • 字段声明的顺序
  • 字段名称
  • 字段类型
  • 字段的修饰符

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

方法信息

  • 方法名称
  • 方法返回类型
  • 方法参数的数量和类型
  • 方法的修饰符
  • 方法的字节码、操作数栈、局部变量表及大小(abstract和native除外)
  • 异常表:每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类和常量池引用。(abstract 和 native 除外)

方法表

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

运行时常量池

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

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

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

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

什么是运行时常量池

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

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

imagen.png

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

字面量 :

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

符号引用:

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

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

简单来说就是:JVM在完成类装载操作后,会将class文件中的常量载入到内存中,并保存在方法区。而放在方法区里的这块内存被称为运行时常量池。

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

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

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

¿Por qué se coloca el grupo de constantes de cadena en el montón?

En jdk7, el grupo de constantes de cadena se coloca en el espacio del montón. Debido a que la eficiencia de recuperación de la generación permanente es baja, solo se activará cuando se use el gc completo, y el gc completo se activará cuando el espacio del antiguo la generación es insuficiente y la generación permanente es insuficiente. Esto conduce a un reciclaje ineficiente del conjunto de constantes de cadena. En nuestro desarrollo, se creará una gran cantidad de cadenas y la baja eficiencia de reciclaje conducirá a una memoria insuficiente en la generación permanente. Ponerlo en el montón puede recuperar la memoria a tiempo.

Observaciones finales

El área de método sobre cómo ejecutar el área de datos se presenta aquí, gracias por leer. Si hay algo mal en el texto, indíquelo en el área de comentarios, gracias nuevamente.

serie de artículos

Supongo que te gusta

Origin juejin.im/post/7086113214236196894
Recomendado
Clasificación