深入JAVA虚拟机-第二版

第一章 JAVA体系结构介绍

  • java应用程序可以使用两种类装载器,启动(bootstrap)类装载器和用户自定义装载器。
    每一个类被装载的时候,java虚拟机都监视着这个类,看它是被什么装载器装载的,当被装载的类引用了其他类的时候,
    会使用相同的类装载器去装载被引用的类。因此默认情况下只能看到被同一个类装载器装载的类,通过这种方法,java允许
    在java程序中简历多个命名空间,每一个类加载器都有自己的命名空间。
  • class 文件,运行在JVM上的二进制文件

第三章 安全(--看完再回顾本章)

  • JAVA沙箱中类装载体系结构是第一道防线
  • 防止恶意代码干涉善意代码
  • 守护被信任类库的边界
  • 将代码分类,确定该类代码可以执行些些操作
  • class 文件检测器
  • 检验字节码的的完整性
  • 类型数据语义检查
  • 字节码验证
  • 符号引用验证
  • 二进制兼容
  • JVM内置安全特性
  • 类型安全的引用转换
  • 结构化的内存访问
  • 自动垃圾收集
  • 数组边界检查
  • 空引用检查
  • 禁止对内存进行非结构化访问
  • 异常的结构化处理
  • 安全管理器和java api
  • 代码签名和认证,将一个未签名的class文件通过散列和私钥得到一个带有签名后散列的文件
  • 访问控制器

第五章 java虚拟机

  • 生命周期,main作为函数的起点,每一个程序都运行一个虚拟机实例上。java虚拟机上两种线程,一种是守护线程,另一种是非守护线程
    守护线程是由jvm自己使用的,比如垃圾回收的线程,而开始于main函数的线程是非守护线程。只要还有非守护线程在运行,虚拟机还是存活的
    ,所有非守护线程退出时,虚拟机会自动退出。

java 虚拟机的结构体系

                             -------------------
                            |   类装载器子系统    |           
                             -------------------
                                     |
    ------------------------------------------------------------------
    |    |        |         |              |                   |      |
    |  方法区      堆       java栈        PC寄存器            本地方法栈  |
    ------------------------------------------------------------------
          |                             |
       执行引擎    ————————————————>  本地方法接口

  • 一些点
  • 方法区和堆内存是由所有的线程共享的,虚拟机装载class文件时,会把类的数据放在方法区中,当程序运行时,会把根据类数据创建的对象
    放在堆内存中。
  • 当新线程被创建时,都将得到一个PC寄存器和一个JAVA栈PC寄存器总是指向下一条被执行的指令,java栈则存储方法的调用状态。(局部变量,参数返回值,运算中间值)
  • java 栈是有很多的栈帧组成,当线程调用一个方法的时候,,JVM将新的栈帧入栈,当函数返回时,栈帧被出栈。
  • 数据类型
  • 基本类型(float, double, byte, short, int, long, char, boolean, returnAddress)
    64 32 8 16 32 64 16
  • 引用类型(类,接口 ,数组)

方法区

  • 方法区的数据共享,因此是必须线程安全的。虚拟机允许程序员指定方法区的大小
  • 方法区会在内存中存储类的以下信息:
  • 类型的全限定名(包名+.+类名)

  • 此类超类的全限定名

  • 类还是接口

  • 修饰符(public ,abstruct, final)

  • 接口全限定名的有序列表

  • 类型的常量池

  • 字段信息(字段名,类型,修饰符)

  • 方法信息(方法名,类型,修饰符)

  • 一个到类ClassLoder的引用

  • 一个到Class类的引用(forName(),让用户得到已装载对象的Class实例)

  • 方法字节码

  • 操作数栈和该方法在栈帧中局部变量的大小

  • 异常表

  • 下面看一看流程

class Volcano(){
    public static void main(String args[]){
        Lava lava = new Lava();
        lava.flow();
    }
}
  • 例如我们要执行一个叫Volcano的类,当我们告诉JVMVolcano这个名字
  • JVM会找到并读入Volcano.class文件
  • 然后导入其中的二进制流,并把相应的数据存在方法区
  • 通过执行方法区中的字节码,开始执行main()方法,在执行时会一直持有指向常量池的指针
  • main方法中第一条指令是给常量池第一个类分配象的内存,于是在常量池中找到第一项,发现是对另一个类Lava的引用
  • 检查方法区,看是否被装载,发现没有被装载,于是查找Lava.class文件装载,同样把信息存储在方法区中
  • 接着指向常量池第一行的指针替换掉常量池的第一项(Lava的全限定名)-> 常量池的解析(符号引用替换成直接引用,本地指针)
  • 虚拟机准备为Lava分配内存,并用这个指针访问它的类信息,找出类信息中的需要为这个类分配多少堆内存
  • 虚拟机确定了大小,就在堆上为对象分配内存,并初始化常量值。然后把对象的引用入栈,第一条指令执行完成。
  • 接下来通过这个引用调用flow方法

深入JAVA虚拟机-第二版_第1张图片
两种堆设计
  • java一个虚拟实例中只有一个堆空间,所有线程共享

  • java 只有在堆中分配内存的指令,没有释放的指令,垃圾收集器会负责堆和方法区的内存回收

  • 堆和方法区一样也不是一个连续的内存区,是可扩展的

  • 堆上的对象还有一部分数据,是对象锁(互斥锁),请求可以追加,请求几次,必须释放几次,例如请求了4次,只释放了3次,那还是持有这个对象锁。

  • 堆上对象还有一部分数据与垃圾收集器有关,垃圾回收器必须跟踪每个对象。

  • 数组是一个对象,总是存储在堆中

  • PC(程序计数器)寄存器,在每个线程中有一个,它有一个字的大小,存储一个本地指针,总是指向下一条将要执行的指令。

java 栈

深入JAVA虚拟机-第二版_第2张图片
过程
  • 每当启用一个线程,JVM会为它分配一个java 栈。
  • 栈帧的组成
  • 局部变量区 -> 一个数组,以字(32bit)为单位,类型int,float,returnAddress的值再数组中占一项,byte,short,char会被转化成int,也占一项,double,long占2项。任何一个实例方法(非static),的数组第一项都是对象本身的reference.
  • 操作数栈 -> byte,short,char会被转化成int,和上面一样,只有在存回堆中是会被转化为原来类型。他也是一个数组,但是按照栈操作来访问。由于java虚拟机没有寄存器,java的指令是通过操作数栈中取得操作数的。
  • 栈帧数据,支持常量池的解析正常方法返回和异常派发机制的数据。
    为了处理执行期的异常退出情况,帧数据区保存一个对此方法的异常表的引用。当方法抛出异常,会在异常表中查找对应的异常,如果找到了匹配的catch语句,就会交给ccatch中的代码处理,如果没有则异常中止。
  • 两站不同的虚拟机实现的帧分配


    深入JAVA虚拟机-第二版_第3张图片
深入JAVA虚拟机-第二版_第4张图片
  • 本地方法栈,取决于设计者的实现

执行引擎(没看太明白)

  • 运行中java程序的每一个线程都是一个独立的虚拟机执行引擎的实例。
  • 从线程的生命周期开始,它要么在执行字节码,要么在执行native方法。
  • 指令集,指令集关注的中心是操作数栈。操作数栈中的数值必须按照适合他们类型的方式使用。比如压入栈4个int,却把他们当做两个long来做操作,是非法的 。

第六章 JAVA class文件 (粗略看)

  • java class文件是对java程序二进制文件的精确定义。一个class文件中指包括一个class 或者interface.

  • class文件不一定与java语言有关,别的语言也可以编译成class文件在虚拟机上运行。

  • 深入JAVA虚拟机-第二版_第5张图片
  • class 文件组成
  • magic ,每个class文件的前四个字节 0xCAFEBABE,用来分辨是否是class文件。
  • minor_version major_version b版本号。
  • constant_pool_count 常量池的数量,constant_pool 常量池
  • access_flags 类的修饰信息,public ,private ,static 等
  • this_class 指向常量池的索引
  • super_class 超类常量池索引
深入JAVA虚拟机-第二版_第6张图片

第七章 类型的生命周期

  • 类主动装载的时机
  • 创建实例时(new, 反射,克隆,反序列化)
  • 调用类中的静态方法
  • 使用类或接口的静态字段或者对字段赋值(final 修饰的静态变量除外,它被初始化为一个编译时的常量表达式)
  • 调用api反射方法
  • 初始化子类
  • 含有main函数的类
  • 所有类的初始化都要求它的超类在此之前初始化了。(接口不是)
  • 装载
  • 通过完全限定名产生一个二进制流
  • 解析二进制文件
  • 创建一个该类型Class实例
  • 初始化
  • 所有类变量初始化语句和类型的静态初始化器都被java编译器收集到一起放在一个特殊的方法中,并非所有的类都有这个方法。
  • 如果多个线程需要初始化一个类,仅仅允许一个线程来执行初始化,其他线程需要等待。完成后需要通知其他等待线程。
对象生命周期
  • 实例化
  • new

  • reflect.newInstance()

  • clone()

  • ObjectInputStream.getObject

  • 堆中分配内存,然后赋予实例变量初始值

  • 类型当没有引用的时候会被卸载,同样通过垃圾回收器。

第八章 链接模型

你可能感兴趣的:(深入JAVA虚拟机-第二版)