方法区中保存的数据
方法区中具体保存了哪些东西?
- 类型信息(包括类、接口、枚举、注解类)
- 类的完整有效名(包名+类名)
- 这个类的父类的完整有效名(接口和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对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
- 对于永久代的GC主要回收的是常量池中废弃的常量和不再使用的类型。判断常量是否废弃比较简单,而要判断一个类型是否属于“不再被使用的类”的条件比较苛刻。需要同时满足三个条件,而满足了三个条件也仅仅是允许被回收。
从字节码中看方法区保存的数据
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"