JVM 方法区的内存结构

方法区中保存的数据

方法区中具体保存了哪些东西?

  • 类型信息(包括类、接口、枚举、注解类)
    • 类的完整有效名(包名+类名)
    • 这个类的父类的完整有效名(接口和Object没有父类)
    • 类的修饰符(public、abstract、final的某个子集)
    • 这个连续直接实现的接口的有序列表
  • 常量(运行时常量池)
    • 每个类都有常量池,类的常量池在运行的时候会加入到方法区的运行时常量池
  • 静态变量
  • JIT即时编译的代码缓存
  • 域(Field)信息(类变量或者叫属性)
    • 类中所有域的相关信息以及域的声明顺序
    • 域名称、域类型、
    • 域的修饰符(public、status、final、volatile、transient的某个子集)
  • 方法信息(按声明的顺序)
    • 方法的名称
    • 方法的返回类型(void也属返回类型的一种)
    • 方法参数的数量和类型(按顺序)
    • 方法的修饰符(public、private、protected、static、final、synchronized、native、abstract的某个一个子集)
    • 方法的字节码、操作数栈的深度、局部变量表深度(abstract和native方法除外)
    • 异常表(abstract和native方法除外)
      • 每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址。被捕获的异常类在常量池中的索引

运行时常量池

        运行时常量池:方法区的一部分
        常量池表:class文件的一部分,用于存放编译器生成的各种字面量和符号引用,这部分的内容将在类加载后存放到方法区的运行时常量池中

        JVM为每个已加载的类型(类或接口)都维护一个常量池,池中的数据就像数组一样,通过索引访问

        运行时常量池中包含多种不同的常量,包括编译期就已经明确的数值字面量,也包括到运行期期解析才能获得的方法或字段的引用。此时不再是常量池中的符号地址,而是真实的地址

        运行时常量池,相对于class文件中常量池表的另一大重要特征是:具备动态性。可以通过String.intern()加入运行时常量池

        当创建类或接口的运行时常量池时,如果构造运行时常量池所需要的内存空间大于方法区所能提供的最大值,JVM就会抛出OOM异常

方法区的演进

  • JDK6:实现永久代,静态变量存放在永久代
  • JDK7:实现永久代,但是已经开始着手“去永久代”,静态变量、字符串常量池已经从永久代转移到堆中
  • JDK8:实现元空间,类型信息、方法、字段、常量保存到本地内存的元空间,但字符串常量池、静态变量还是保存在堆中

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

  • 永久代的大小很难确定
    • 某一些情况下动态加载的类过多,容易产生Perm区的OOM,而元空间使用的是本地内存,对于最大内存没有限制。
  • 对于永久代进行调优困难
    • 对于永久代的GC主要回收的是常量池中废弃的常量和不再使用的类型。判断常量是否废弃比较简单,而要判断一个类型是否属于“不再被使用的类”的条件比较苛刻。需要同时满足三个条件,而满足了三个条件也仅仅是允许被回收
      • 条件一:该类所有的实例已被回收,包括该类和其派生子类
      • 条件二:该类的类加载器已经被回收,通常该条件很难达成
      • 条件三:该类对于的class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法

从字节码中看方法区保存的数据

import java.io.Serializable;

public class kong extends Test implements Serializable {

    public int a = 10;
    private int b = 11;

    public static int A = 20;
    private static final int B = 21;

    public Integer test(int num){
        try {
            num += 1;
        }catch (Exception e){
            e.printStackTrace();
        }
        return num;
    }

    public static String test_1(String str){
        return str;
    }

    public static void main(String[] args) {
        String s = test_1("a");
    }

}
Classfile /E:/code/text/text/target/classes/kong.class
  Last modified 2022-4-13; size 1039 bytes
  MD5 checksum 505c81d175d800b2bdfad78d7c521f6c
  Compiled from "kong.java"

  // 类型信息 继承的类 实现的接口
public class kong extends Test implements java.io.Serializable
  minor version: 0
  major version: 52
  // 类的修饰符
  flags: ACC_PUBLIC, ACC_SUPER

  // 常量池
Constant pool:
   #1 = Methodref          #11.#46        // Test."<init>":()V
   #2 = Fieldref           #10.#47        // kong.a:I
   #3 = Fieldref           #10.#48        // kong.b:I
   #4 = Class              #49            // java/lang/Exception
   #5 = Methodref          #4.#50         // java/lang/Exception.printStackTrace:()V
   #6 = Methodref          #51.#52        // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #7 = String             #13            // a
   #8 = Methodref          #10.#53        // kong.test_1:(Ljava/lang/String;)Ljava/lang/String;
   #9 = Fieldref           #10.#54        // kong.A:I
  #10 = Class              #55            // kong
  #11 = Class              #56            // Test
  #12 = Class              #57            // java/io/Serializable
  #13 = Utf8               a
  #14 = Utf8               I
  #15 = Utf8               b
  #16 = Utf8               A
  #17 = Utf8               B
  #18 = Utf8               ConstantValue
  #19 = Integer            21
  #20 = Utf8               <init>
  #21 = Utf8               ()V
  #22 = Utf8               Code
  #23 = Utf8               LineNumberTable
  #24 = Utf8               LocalVariableTable
  #25 = Utf8               this
  #26 = Utf8               Lkong;
  #27 = Utf8               test
  #28 = Utf8               (I)Ljava/lang/Integer;
  #29 = Utf8               e
  #30 = Utf8               Ljava/lang/Exception;
  #31 = Utf8               num
  #32 = Utf8               StackMapTable
  #33 = Class              #49            // java/lang/Exception
  #34 = Utf8               test_1
  #35 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #36 = Utf8               str
  #37 = Utf8               Ljava/lang/String;
  #38 = Utf8               main
  #39 = Utf8               ([Ljava/lang/String;)V
  #40 = Utf8               args
  #41 = Utf8               [Ljava/lang/String;
  #42 = Utf8               s
  #43 = Utf8               <clinit>
  #44 = Utf8               SourceFile
  #45 = Utf8               kong.java
  #46 = NameAndType        #20:#21        // "<init>":()V
  #47 = NameAndType        #13:#14        // a:I
  #48 = NameAndType        #15:#14        // b:I
  #49 = Utf8               java/lang/Exception
  #50 = NameAndType        #58:#21        // printStackTrace:()V
  #51 = Class              #59            // java/lang/Integer
  #52 = NameAndType        #60:#28        // valueOf:(I)Ljava/lang/Integer;
  #53 = NameAndType        #34:#35        // test_1:(Ljava/lang/String;)Ljava/lang/String;
  #54 = NameAndType        #16:#14        // A:I
  #55 = Utf8               kong
  #56 = Utf8               Test
  #57 = Utf8               java/io/Serializable
  #58 = Utf8               printStackTrace
  #59 = Utf8               java/lang/Integer
  #60 = Utf8               valueOf
{

    // 类变量a
  public int a; // 名称
    descriptor: I   //类型
    flags: ACC_PUBLIC  //修饰符

    // 类变量b
  private int b;
    descriptor: I
    flags: ACC_PRIVATE  //修饰符

    // 静态变量A
  public static int A;
    descriptor: I   //类型
    flags: ACC_PUBLIC, ACC_STATIC   //修饰符

    // 静态常量B
  private static final int B;
    descriptor: I
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL   //修饰符
    ConstantValue: int 21   //常量赋值

    // 空参构造器 <init>方法
  public kong();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
    // 操作数栈深度 局部变量表深度 参数个数
      stack=2, locals=1, args_size=1
      // 字节码
         0: aload_0
         1: invokespecial #1                  // Method Test."<init>":()V
         4: aload_0
         5: bipush        10
         7: putfield      #2                  // Field a:I
        10: aload_0
        11: bipush        11
        13: putfield      #3                  // Field b:I
        16: return
      LineNumberTable:
        line 3: 0
        line 5: 4
        line 6: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      17     0  this   Lkong;

// test方法
  public java.lang.Integer test(int);   // 返回值类型 参数类型
    descriptor: (I)Ljava/lang/Integer;
    // 修饰符
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=3, args_size=2
         0: iinc          1, 1
         3: goto          11
         6: astore_2
         7: aload_2
         8: invokevirtual #5                  // Method java/lang/Exception.printStackTrace:()V
        11: iload_1
        12: invokestatic  #6                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        15: areturn
        //异常表
      Exception table:
      // 开始位置 结束位置 出现异常的处理位置
         from    to  target type
             0     3     6   Class java/lang/Exception
      LineNumberTable:
        line 13: 0
        line 16: 3
        line 14: 6
        line 15: 7
        line 17: 11
        //局部变量表
      LocalVariableTable:
      // 起始位置 结束位置 占用slot大小 名称 类型
        Start  Length  Slot  Name   Signature
            7       4     2     e   Ljava/lang/Exception;
            0      16     0  this   Lkong;
            0      16     1   num   I
      StackMapTable: number_of_entries = 2
        frame_type = 70 /* same_locals_1_stack_item */
          stack = [ class java/lang/Exception ]
        frame_type = 4 /* same */

//test_1方法
  public static java.lang.String test_1(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: areturn
      LineNumberTable:
        line 21: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       2     0   str   Ljava/lang/String;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #7                  // String a
         2: invokestatic  #8                  // Method test_1:(Ljava/lang/String;)Ljava/lang/String;
         5: astore_1
         6: return
      LineNumberTable:
        line 25: 0
        line 26: 6
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  args   [Ljava/lang/String;
            6       1     1     s   Ljava/lang/String;

// <clinit>方法 为静态常量赋值
  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: bipush        20
         2: putstatic     #9                  // Field A:I
         5: return
      LineNumberTable:
        line 8: 0
}
SourceFile: "kong.java"
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值