Java内存的方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。本章深入了解下这些个部分是什么。
ClassLoader加载class文件和存储文件信息的流程
当一个classLoder启动的时候,classLoader的生存地点在jvm中的堆,然后它会去主机硬盘上将A.class装载到jvm的方法区,方法区中的这个字节文件会被虚拟机拿来new A字节码(),然后在堆内存生成了一个A字节码的对象,A字节码这个内存文件有两个引用一个指向A的class对象,一个指向加载自己的classLoader。
方法区关键信息
-
类信息:
- 修饰符(public final)
- 是类还是接口(class,interface)
- 类的全限定名(Test/ClassStruct.class)
- 直接父类的全限定名(java/lang/Object.class)
- 直接父接口的权限定名数组(java/io/Serializable)
定义类的描述信息的提取
public final class ClassA extends Object implements Serializable
-
字段信息:
- 修饰符(pirvate)
- 字段类型(java/lang/String.class)
- 字段名(name)
定义字段的描述信息提取
private String name;
-
方法信息:
- 修饰符(public static final)
- 方法返回值(java/lang/String.class)
- 方法名(getStatic_str)
- 参数需要用到的局部变量的大小还有操作数栈大小(详见虚拟机栈中操作数栈)
- 方法体的字节码(编译后花括号里的内容)
- 异常表
定义方法的字节码信息提取
public static final String functionA ()throws Exception{ System.out.println("a"); }
-
常量池
- 直接常量(各种基本数据类型基础常量池)
- 方法名、方法描述符、类名、字段名,字段描述符的符号引用
所有编译器能够被确定,能够被快速查找的内容都存放在这里,它像数组一样通过索引访问,就是专门用来做查找的。 编译时就能确定数值的常量类型都会复制它的所有常量到自己的常量池中(如static变量),或者嵌入到它的字节码流中。作为常量池或者字节码流的一部分,编译时常量保存在方法区中,就和一般的类变量一样。但是当一般的类变量作为他们的类型的一部分数据而保存的时候,编译时常量作为使用它们的类型的一部分而保存
-
类变量: 静态字段,虚拟机在使用某个类前,必须在方法区为这些变量分配空间
-
一个到classLoader的引用
-
一个到类的class对象的引用 这个对象存储了所有这个字节码内存块的相关信息,所有在字节码中你想得到的,调用的,通过class这个引用基本都能够帮你完成。因为他就是字节码在内存块在堆中的一个对象
-
方法表 方法表是JVM对于
虚分派(virtual dispatch
的一种实现。 虚分派机制首先会从被调用方法的对象类的实现中查找对应的方法,如果没找到则去父类查找,直到找到函数并实现调用,而不是依赖于引用的类型。 方法表的内容就是这个类的所有实例可能被调用的所有实例方法的直接引用。也是为了动态绑定的快速定位而做的一个类似缓存的查找表,它以数组的形式存在于内存中。不过这个表不是必须存在的,取决于虚拟机的设计者,以及运行虚拟机的机器是否有足够的内存。