最近读周志明的《深入理解Java虚拟机》以及半栈工程师的Java虚拟机文章,对JVM又重新复习了一遍,每次看后收获都不一样(因为没有debug能力,还是很懵懂),担心会忘记将自己读后总结记录下来
java先编译成二进制形式的java字节码放在Class文件中
通过jclasslib bytecode viewer插件查看class文件基本格式

2.1 Class类文件的结构(参考《Java虚拟机规范》)
(1)魔数与Class文件的版本
(2)常量池
(3)访问标志
(4)类索引、父类索引与接口索引集合 // 继承关系
(5)字段表集合
(6)方法表集合
(7)属性表集合
当运行过程中需要这个类时进行类加载
3.1 配置-XX:+TraceClassLoading JVM参数监控类的加载 
3.2 当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先初始化这个主类
3.3 利用ClassFileStream加载class文件转成文件流
3.4 调用ClassFileParser解析文件流生成JVM的数据结构InstanceKlass
(1)在Metaspace(元空间)为 InstanceKlass 分配内存
(2)分析Class文件,填充 InstanceKlass 内存区域
3.5 Dictionary保存ClassLoader加载过的类信息
调用主类的main方法
4.1 分配方法对应的Method,将解析方法时读取到信息填充到Method中
4.2 在调用类的static方法时初始化InstanceKlass,调用 link_class()对类进行链接
4.3 最终是通过StubRoutines::call_stub()的返回值来调用java方法的
4.4 通过method找到对应的entry_point例程,并传递给call_stub例程
(1)当方法链接时,会去设置方法的entry_point
(2)例程可以理解为用汇编写好的一个方法
4.5 call_stub准备好堆栈后,就开始前往entry_point处
4.6 entry_point例程就会开始执行传递给它的Java方法了

Java虚拟机以方法作为最基本的执行单元,“栈帧”(Stack Frame)则是用于支持虚拟机进行方法 调用和方法执行背后的数据结构

局部变量表:用于存放方法参数和方法内部定义的局部变量
1.1 在方法的Code属性的max_locals数据项中确定了该方 法所需分配的局部变量表的最大容量
1.2 reference类型表示对一个对象实例的引用
(1)根据引用直接或间接地查找到对象在Java堆中的数据存放的起始地址或索引
(2)根据引用直接或间接地查找到对象所属数据类型在方法区中的存储的类型信息
1.3 returnAddress类型已经全部改为采用异常表来代替了
1.4 如果执行的是实例方法,那局部变量表中第0位索引的变量槽默认是用于传递方法所属对象实例的引用
操作数栈
2.1 操作数栈的最大深度也在编译的时候被写入到Code属性的max_stacks数据项之中
2.2 操作数栈中元素的数据类型必须与字节码指令的序列严格匹配

动态链接
3.1 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用
3.2 字节码中的方法调用指令就以常量池里指向方法的符号引用作为参数



org.apache.lucene
lucene-core
4.0.0
//计算指定对象及其引用树上的所有对象的综合大小,单位字节
long RamUsageEstimator.sizeOf(Object obj)
//计算指定对象本身在堆空间的大小,单位字节
long RamUsageEstimator.shallowSizeOf(Object obj)
//计算指定对象及其引用树上的所有对象的综合大小,返回可读的结果,如:2KB
String RamUsageEstimator.humanSizeOf(Object obj)
虽然说没有debug Java虚拟机的能力,但在看相关书籍和文章前对自己提出相关问题,带着目的性去阅读还是能解答很多自己的内心的疑惑,后面继续沉淀吧