目录

栈、堆、方法区的交互关系

方法区的理解

设置方法区内存的大小

方法区的内部结构

概述

类型信息

域信息(Field)

方法信息

静态变量

常量池表和运行时常量池

比较

常量池表

运行时常量池

总结:方法区存储了什么

方法区历代版本的演进

历代版本图

为什么要把永久代换成元空间?

StringTable为什么要调整

方法区的垃圾回收

 


 

 

栈、堆、方法区的交互关系

  • 运行时数据区结构图

java中方法区 java方法区在哪_jvm

java中方法区 java方法区在哪_java中方法区_02

  •  我们学习的HotSpot用的是左图这种直接指针的方式,但是还有一种句柄池的方式(《深入理解Java虚拟机》),如右图所示

java中方法区 java方法区在哪_常量池_03

java中方法区 java方法区在哪_jvm_04

方法区的理解

  • JDK1.7之前方法区又叫永久代PermGen space,1.7之后叫做元空间Metaspace, 元空间使用的是本地内存,不使用jvm的内存。

java中方法区 java方法区在哪_java_05

java中方法区 java方法区在哪_方法区_06

java中方法区 java方法区在哪_java_07

java中方法区 java方法区在哪_java中方法区_08

java中方法区 java方法区在哪_java_09

 

设置方法区内存的大小

  • jdk7之后这个指令是无效的,因为已经替换成元空间了

java中方法区 java方法区在哪_方法区_10

  • jdk8及以后都是设置元空间命令 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 指定

java中方法区 java方法区在哪_常量池_11

方法区的内部结构

概述

java中方法区 java方法区在哪_java中方法区_12

java中方法区 java方法区在哪_java中方法区_13

  • 字节码文件如下
D:\buildproject\jvm-study\out\production\jvm-01\com\vuhen\jvmMethod>javap -v -p  MethodDemo.class
Classfile /D:/buildproject/jvm-study/out/production/jvm-01/com/vuhen/jvmMethod/MethodDemo.class
  Last modified 2021-5-12; size 1095 bytes
  MD5 checksum 0f0987d697ef358a1a47c75699f74abf
  Compiled from "MethodDemo.java"

//类型信息
public class com.vuhen.jvmMethod.MethodDemo extends java.lang.Object implements java.util.Comparator<java.lang.String>
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
//常量池
Constant pool:
   #1 = Methodref          #8.#38         // java/lang/Object."<init>":()V
   #2 = Fieldref           #7.#39         // com/vuhen/jvmMethod/MethodDemo.publicVal:I
   #3 = Fieldref           #7.#40         // com/vuhen/jvmMethod/MethodDemo.priVal:I
   #4 = Class              #41            // java/lang/String
   #5 = Methodref          #7.#42         // com/vuhen/jvmMethod/MethodDemo.compare:(Ljava/lang/String;Ljava/lang/String;)I
   #6 = Fieldref           #7.#43         // com/vuhen/jvmMethod/MethodDemo.priStaticVal:I
   #7 = Class              #44            // com/vuhen/jvmMethod/MethodDemo
   #8 = Class              #45            // java/lang/Object
   #9 = Class              #46            // java/util/Comparator
  #10 = Utf8               publicVal
  #11 = Utf8               I
  #12 = Utf8               priStaticVal
  #13 = Utf8               priVal
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               LocalVariableTable
  #19 = Utf8               this
  #20 = Utf8               Lcom/vuhen/jvmMethod/MethodDemo;
  #21 = Utf8               compare
  #22 = Utf8               (Ljava/lang/String;Ljava/lang/String;)I
  #23 = Utf8               o1
  #24 = Utf8               Ljava/lang/String;
  #25 = Utf8               o2
  #26 = Utf8               priMethod
  #27 = Utf8               (I)I
  #28 = Utf8               a
  #29 = Utf8               i
  #30 = Utf8               b
  #31 = Utf8               publicStaticMethod
  #32 = Utf8               (Ljava/lang/Object;Ljava/lang/Object;)I
  #33 = Utf8               <clinit>
  #34 = Utf8               Signature
  #35 = Utf8               Ljava/lang/Object;Ljava/util/Comparator<Ljava/lang/String;>;
  #36 = Utf8               SourceFile
  #37 = Utf8               MethodDemo.java
  #38 = NameAndType        #14:#15        // "<init>":()V
  #39 = NameAndType        #10:#11        // publicVal:I
  #40 = NameAndType        #13:#11        // priVal:I
  #41 = Utf8               java/lang/String
  #42 = NameAndType        #21:#22        // compare:(Ljava/lang/String;Ljava/lang/String;)I
  #43 = NameAndType        #12:#11        // priStaticVal:I
  #44 = Utf8               com/vuhen/jvmMethod/MethodDemo
  #45 = Utf8               java/lang/Object
  #46 = Utf8               java/util/Comparator
{
  //成员变量 域信息
  public int publicVal;
    descriptor: I //类型int
    flags: ACC_PUBLIC //修饰符public

  private static int priStaticVal;
    descriptor: I //类型int
    flags: ACC_PRIVATE, ACC_STATIC //修饰符private,static

  private int priVal;
    descriptor: I//类型int
    flags: ACC_PRIVATE //修饰符private

  //构造器
  public com.vuhen.jvmMethod.MethodDemo();
    descriptor: ()V //()表示参数为空 V表示返回值为void
    flags: ACC_PUBLIC //修饰符public
    Code:
      //栈深度(栈执行中最大的长度);局部变量表的大小;参数个数
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: iconst_5
         6: putfield      #2                  // Field publicVal:I
         9: aload_0
        10: iconst_3
        11: putfield      #3                  // Field priVal:I
        14: return
      LineNumberTable:
        line 5: 0
        line 6: 4
        line 8: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      15     0  this   Lcom/vuhen/jvmMethod/MethodDemo;

  //方法信息
  public int compare(java.lang.String, java.lang.String);
    descriptor: (Ljava/lang/String;Ljava/lang/String;)I //参数String;String 返回值int
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=3, args_size=3
         0: iconst_0
         1: ireturn
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       2     0  this   Lcom/vuhen/jvmMethod/MethodDemo;
            0       2     1    o1   Ljava/lang/String;
            0       2     2    o2   Ljava/lang/String;

  public int priMethod(int);
    descriptor: (I)I //参数int 返回值int
    flags: ACC_PUBLIC //修饰符public
    Code:
      stack=2, locals=4, args_size=2
         0: iconst_5
         1: istore_2
         2: iconst_4
         3: istore_3
         4: iload_2
         5: iload_3
         6: iadd
         7: iload_1
         8: iadd
         9: ireturn
      LineNumberTable:
        line 15: 0
        line 16: 2
        line 17: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   Lcom/vuhen/jvmMethod/MethodDemo;
            0      10     1     a   I
            2       8     2     i   I
            4       6     3     b   I

  public static void publicStaticMethod();
    descriptor: ()V //无参数无返回值
    flags: ACC_PUBLIC, ACC_STATIC //修饰符public static
    Code:
      stack=1, locals=1, args_size=0
         0: iconst_5
         1: istore_0
         2: return
      LineNumberTable:
        line 21: 0
        line 22: 2
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            2       1     0     i   I

  public int compare(java.lang.Object, java.lang.Object);
    descriptor: (Ljava/lang/Object;Ljava/lang/Object;)I //参数Object,Object 返回值int
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC //修饰符
    Code:
      stack=3, locals=3, args_size=3
         0: aload_0
         1: aload_1
         2: checkcast     #4                  // class java/lang/String
         5: aload_2
         6: checkcast     #4                  // class java/lang/String
         9: invokevirtual #5                  // Method compare:(Ljava/lang/String;Ljava/lang/String;)I
        12: ireturn
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0  this   Lcom/vuhen/jvmMethod/MethodDemo;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: iconst_4
         1: putstatic     #6                  // Field priStaticVal:I
         4: return
      LineNumberTable:
        line 7: 0
}
Signature: #35                          // Ljava/lang/Object;Ljava/util/Comparator<Ljava/lang/String;>;
SourceFile: "MethodDemo.java"

类型信息

java中方法区 java方法区在哪_常量池_14

域信息(Field)

java中方法区 java方法区在哪_方法区_15

方法信息

java中方法区 java方法区在哪_java中方法区_16

静态变量

  • 静态变量是和类绑定在一起的,即使没有实例也能够访问,下面的代码是能运行的,不会报错
  • 注:静态变量仍存放在堆中

java中方法区 java方法区在哪_常量池_17

java中方法区 java方法区在哪_方法区_18

常量池表和运行时常量池

比较

  • 常量池表是字节码的一部分,运行时常量池是方法区的一部分 常量池表中的内容会在类加载完后放到运行时常量池中
  • 在运行后就会生成运行时常量池,而字节码中的常量池表可以理解为就是运行时常量池中信息的索引

java中方法区 java方法区在哪_jvm_19

java中方法区 java方法区在哪_常量池_20

常量池表

  • 一个小小的类里面其实使用了非常多的引用和类,但是下面生成的字节码却非常小,原因就是因为有常量池表,用来存储对类型、域和方法符号引用以及各种字面量

java中方法区 java方法区在哪_java中方法区_21

  • 下面这个字节码的大小就说明了常量池表的存在

java中方法区 java方法区在哪_jvm_22

  • 如下图所示,方法执行时都是调用常量池表的符号引用,符号引用再到常量池中找信息

java中方法区 java方法区在哪_方法区_23

java中方法区 java方法区在哪_常量池_24

  • ldc最终找到的常量池中信息就是ceshi1 这个称为字面量

java中方法区 java方法区在哪_方法区_25

java中方法区 java方法区在哪_方法区_26

运行时常量池

  • 常量池表在类加载后就会把信息全部都加载到运行时常量池;运行时常量池时具有动态性的 运行期间也可能产生新的常量,这些常量被放在运行时常量池中。

java中方法区 java方法区在哪_java_27

总结:方法区存储了什么

  • 像什么常量池啊,类信息,域信息,方法信息,方法中的指令信息等等都是存在方法区的

java中方法区 java方法区在哪_常量池_28

java中方法区 java方法区在哪_java中方法区_29

方法区历代版本的演进

历代版本图

  • JDK8之前方法区用的还是虚拟机的内存,8及以后用的就是本地内存了
  • StringTable指的是字符串常量池

java中方法区 java方法区在哪_方法区_30

java中方法区 java方法区在哪_常量池_31

java中方法区 java方法区在哪_常量池_32

为什么要把永久代换成元空间?

java中方法区 java方法区在哪_方法区_33

java中方法区 java方法区在哪_方法区_34

java中方法区 java方法区在哪_方法区_35

StringTable为什么要调整

java中方法区 java方法区在哪_jvm_36

方法区的垃圾回收

java中方法区 java方法区在哪_方法区_37

java中方法区 java方法区在哪_java_38

java中方法区 java方法区在哪_jvm_39