阅读更多
《深入java虚拟机(原书第二版)》读书笔记 一 Java 虚拟机
(一)Java虚拟机的生命周期
当启动一个Java程序时,一个虚拟机的实例被创建;当程序被关闭,虚拟机的实例被销毁。
(二)Java虚拟机的体系结构
1 类装载器
2 运行时数据区
3 执行引擎
(三) 各部件说明
1 类装载器
1)类装载器分两类:启动类装载器和用户自定义装载器。
2)启动类装载器是JVM实现的一部分。
3)用户自定义装载器一般采用“双亲委派模式”来加载一个类型。他的根双亲一般是启动类装载器。
4)JVM为每一个类装载器实例维护命名空间,相同类装载器实例装载的类型间可以互访问。不同类装载器实例装载的类型间除非经过显式的定义互访机制,否则不能互访。
5)当一个类型A引用类型B,JVM将使用装载类型A的初始类加载器装载类型B.
6)类加载器严格按以下顺序装载类型:
i 定位和导入指定类型的二进制class文件
ii 验证被导入类型的class文件的正确性
iii 为类变量分配内存空间,并将其初始化为默认值
iv 把该类型实例的常量池中的常量(类型的符号引用)转换成直接引用(解析的时机视具体的JVM实现而定)
v 把类变量初始化为正确的初始化值
vi 在堆中生成该类型的java.lang.Class的实例
2 运行时数据区
1)运行时数据区由以下几部分组成:
i 方法区
ii 堆
iii Java栈
iv PC寄存器
v 本地方法栈
2)方法区中存放关于被加载类型的类型信息,类型信息包括:
i 类型的全限定名
ii 类型的直接超类的全限定名(java.lang.Object没有超类)
iii 类型是类还是接口
iv 类型的访问修饰符
v 类型的直接超接口的列表
vi 类型的常量池
vii 类型的字段信息(字段名,类型,修饰符)
viii 类型的所有类静态变量(不包括常量)
viii 一个到类的ClassLoader的引用
x 一个到java.lang.Class类的引用
xi 类型的方法信息(方法的名称,返回值,参数列表,方法的修饰符,方法的字节码,方法的异常表和操作数栈和方法栈帧中局部变量区的大小)
3)方法区也存放被加载类型类的静态变量。
4)一个程序中所有线程都共享方法区。
5)方法区的大小不固定。
6)方法区也要进行垃圾收集,不再被引用的类型所占的区域需要被释放,回收。
7)不同类装载器实例装载的相同类型,在方法区中有不同的空间.
8)堆中存放程序在运行时创建的类实例和数组。
9)一个程序中所有线程共享堆。
10)多线程时要考虑堆的同步问题。
11)堆中需要进行垃圾收集。
12)堆也可以不必是连续的内存空间。
13)堆中的类型实例都有一个指针,这个指针指向在方法区中与之对应类型的类数据。
14)类实例的实力变量在堆中,类型的方法通过方法表指向方法区相关类型的方法信息。
15)每一个线程都有自己的PC寄存器。(在线程创建时创建)。
16)PC寄存器的大小是一个字长(字长根据不同JVM实现,大小不同)。
17)PC寄存器中总是存放下一个指令的地址。
18)如果一个线程正在执行本地方法,那么此时PC寄存器中的内容为“undefined”。
19)Java栈是线程私有的。每一个线程用有一个Java栈。Java栈是每一个Java线程的“工作台”。
20)每当线程调用一个Java方法时,JVM都会在该线中压入一个新帧,这个新帧就是当前帧。
21)一个Java栈的栈帧由以下部分组成:
i 局部变量区
ii 操作数栈
iii 帧数据区
22)Java调用本地方法本质上就是:Java栈和本地方法栈数据的相互交换。
(三)执行引擎
1)运行在Java程序的每一个线程都是一个独立的虚拟机执行引擎的实例。从线程生命周期的开始到结束,它要么在执行字节码,要么在执行本地方法。
二 连接模型
(一)常量池解析
1)常量池解析的最终目的是把对类型的符号引用转换成直接引用。
2)解析对象数组时,先解析并分配数据维数,然后解析数组元素的类型,数组的类装载器会被标记为装载数组元素类型的类装载器。
3)相同类型被不同的类装载器装载时,会在方法区创建不同位置的空间。
4)先装载需要被装载类型,然后再装载这个类型的所有超类型(类,接口)。
5)初始化过程是先初始化这个类型的超类型(只初始化类,不包括接口),然后再初始化这个类型。
6)指向类型,类变量和类方法的直接引用可能是指向方法区的本地指针。
7)指向实例变量和实例方法的直接引用都是偏移量。
8)直接引用第一次被解析成功后,JVM会使用*_quick系列指令进行优化。但是当JVM解析一个通过接口引用调用的方法时,它必须搜索对象的类的方法表来找到一个合适的方法,也就是说对于这种情况,JVM无法进行直接引用指令优化。因为某一个接口方法在具体实现中的偏移量很可能不同。
(二)类装载器的双亲委派模式
使用这种模式进行类型装载,原则是尽量让类装载器的双亲来装载这个类型,双亲和祖先实在装载不了,则自己搞定。
三 内存垃圾收集
1)内存垃圾收集的时间视不同的JVM实现而不同。
2)内存垃圾收集器先找出堆中不能被触及的对象,然后执行这些对象的finalize方法,由于执行完这些对象的finalize方法后,可能存在复活的对象,还需要重新查找不能被触及的对象,然后进行释放。
忙于项目,暂时没有时间看深入jvm,网上看到本篇还不错,先借过来,以后抽时间看了之后,写些体会总结出来!原文- 作者: Rangers