虚拟机学习总结

来源:互联网 发布:银行家算法代码实现 编辑:程序博客网 时间:2024/06/12 20:30

常量池主要存放:字面量,符号引用

字面量(字符串,被声明为final 的常量值)

符号引用(类和接口的全限定名, 字段的名称 和 描述符, 方法的名称和描述符)

被class装载的类型信息

但是Java.Lang.Class的实例存放在内存中,作为访问方法区数据的入口

<!-----------------------------!>

类型信息 对每个装载的类型,虚拟机会在方法区存放以下信息

 

这个类型的全限定名

这个类型的直接超类全限定名

这个类型是类类型还是接口类型

这个类型的访问修饰符(public abstract final)

任何直接超接口的权限定名的有序列表

全限定名(.换为/)eg.  java.Lang.Object    java/Lang/Object

 

还保存基本类型信息

该类型的常量池

字段信息

方法信息

除了常量以外的所有类(静态)变量

一个到类ClassLoader的引用

一个到类Class类的引用

<!-----------------------------!>

编译时刻常量(final声明且用编译时已知的值初始化的类变量),常量复制到常量池或者直接嵌入字节码。

非编译时刻常量在方法区预留空间,等待初始化。

 

 

一个解析的过程

class Lava{private static int speed=5;private int b=10;void flow(){System.out.println(b);}}public class Volcano{public static void main(String [] args){Lava la=new Lava();la.flow();}}


这段代码  首先类加载器加载 Volcano类,然后执行main方法,通过Volcano 方法区数据找到指向类加载器的指针,类加载器发现

Lava类没有加载,然后加载类信息,此时将Volcano 类常量池中对Lava的符号引用转换为对Lava方法区数据的引用,当创建Lava类对象的时候

根据Volcano中的引用得到方法区信息并创建实例。

 

 <!-----------------------------!>

栈:用于处理栈桢的正常压栈出栈

 

栈帧: 局部变量区,操作数栈,帧数据区

 

局部变量区,操作数栈 大小以字为单位 编译期确定,帧数据区则动态确定

 

局部方法区存储 方法的参数 其中 byte short char 和 boolean  都用 int 表示

操作数站 以 出栈和入栈访问 栈方法解释执行的地方

栈数据区 存储一些常量池解析 异常派发等信息。

 

 

 

 

 

 

 

 

 

 

 

<!-----------------------------!>

方法特征签名(名称,参数,参数类型)。

这是java 中的约束,在class 文件中 返回值也可以作为重载的标识

 

 

java是动态语言(动态加载 动态连接)

 

 

类的生命周期(加载 连接(验证 准备 解析)初始化 使用 卸载)

 

加载过程

1.通过全限定名获取类的二进制流

2.把class文件转化为方法区的运行时数据结构

3.在堆中生成Class 对象,作为访问方法区的数据结构

 

 

准备阶段

1.正式为类变量分配内存,并设置类变量初始化

    (final 变量被赋值,其余为0值)

 

 

初始化阶段

 

类初始化

调用cinit  方法   先变量赋值,在静态初始化块

cinit 方法不包括父类 cinit 方法的调用 ,但是子类 cinit 方法调用的时候需要保证父类的也被调用过, 实现接口的类不必要初始化该接口,仅当第一次使用该接口的中属性被用到时

 

主动使用的时候才会进行类初始化

1.调用类中声明的静态方法

2.操作类或接口中声明的非常量静态字段

3.调用特定的反射方法

4.初始化一个类的子类

5.被指定的作为启动时初始化的类

 

 

对象初始化

<init>方法 此方法可能包含三种代码

.调用另一个 <init>,实现对任何实例变量的初始化,构造方法体的代码

如果 init 通过 this调用开始,那么转换为以下两步

1.一个同类的 init 的调用

2.实现了对应构造方法体的字节码

 

如果没有显示使用 init

 

那么分为以下三部

1.一个超类 init 方法的调用

2.任意实例变量初始化方法的字节码

3.实现了对应构造方法的方法体的字节码

 

 与类变量初始化的区别,  父类的 init 由子类调用, 而 类变量的 cinit 是自己调用

 

 

实例化一个类的四种途径

1.明确使用new

2.使用class 或 java.lang.reflect.Constructor 的new Instance方法

3.调用任何现有对象的 clone

4.通过java.io.ObjectInputStream getObject 方法

 

 

 

类加载器

 

双亲委派模型,保证同一个类由同一加载器加载,这样 类Class 对象的 equals  ,isInstance 方法instanceof关键字  返回结果相等

 

启动类加载器

扩展类加载器

应用程序类加载器(默认的)

自定义类加载器

 

收到请求向上委派

 

 

 

运行时栈帧结构

每个线程一个栈 ,每个方法一个栈帧。

栈帧存储了 :局部变量表,操作数栈,动态连接 和 方法返回地址 和一些额外的附加信息

局部变量表 用于存放 方法参数和方法内部定义的局部变量

 

动态连接   每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用 ,解析是把符号引用转换为直接引用。

 

 

解析过程

符合“编译器可知,运行期不可变”  主要有静态方法和私有方法

调用方法的字节码

1.invokestatic  调用静态方法

2.invokespecial 调用实例构造器 init 方法,私有方法和父类方法

3.invokevirtual  所有虚方法

4.invokeinterface调用接口方法,会在运行时刻确定一个实现此接口的对象

 

只要能被 前两指令调用的方法,都可以在解析阶段确定唯一的调用版本

符合此条件的有 : 静态 方法 ,私有方法, 实例构造器 和父类方法 ,他们在类加载的时候会把符号引用解析为该方法的直接引用 这些方法称为非虚方法,与之相反的叫虚方法(除final)