JVM虚拟机结构图
程序计数器
每个线程有单独的程序计数器,程序计数器的指针永远指向下一条要执行的指令
线程私有方法区
类的类型信息,常量池
线程共享Java堆内存
通过new关键字创建的对象实例,都保存在Java堆里面,这一块内存区域是GC回收的主要地方。而创建的对象的类型信息取方法区去取。
线程共享Java栈内存
Java栈总是跟线程关联在一起,每当创建一个线程,JVM就回为线程分配一个Java栈空间,而每个Java栈又包含多个栈帧组成,用于存储局部变量表、操作栈、方法返回值等。
线程私有
对象的创建
JVM在方法区开辟空间存放对象的类信息,在Java堆内存中存放对象的实例信息,然后将引用指针给引用对象。
对象模型图
垃圾回收
http://blog.csdn.net/jiafu1115/article/details/7024323
主要涉及三个问题:
如何判定对象为垃圾对象
- 引用计数器
- 可达性分析算法
如何回收
- 标记清除
- 标记复制
- 标记压缩
- 分代收集算法
- 何时回收
- Serial
- Parnew
- Cms
- G1
内存分配策略
- 优先分配到eden
- 大对象直接分配到老年代
- 长期存活的对象分配到老年代
- 空间分配担保
- 动态对象年龄判断
Class文件
- 魔术 0xCAFEBABE
- Class文件版本
- 常量池
- 访问标志
- 类索引 父类索引 接口索引的集合
- 字段表集合
- 方法表集合
- 属性表集合
https://blog.csdn.net/wangtaomtk/article/details/52267621
字节码指令
基本知识
- 操作码和操作数组成
- 操作码的长度是1个字节,最多256条
- 基于栈的指令集架构
分析命令
javap -verbose xxx.class
指令
iload iconstant
iadd
ireturn大多数指令都包含类型信息,
也有没有 goto- 运算指令
- add 加
- sub 减
- mul 乘
div 除
…类型转换指令
- i2l int 转换成 long
- i2b int 转换成 byte
- i2c int 转换成 char
…
对象创建与访问指令
- 创建类实例的指令 new
- 创建数组的指令 newarray anewarray
- 访问类字段 getfield putfield getstatic putstatic
…
操作数栈管理指令
- 操作数栈指令用于直接操作操作数栈
- pop dup
- swap 栈顶的交换
…
控制转移指令
- 条件分支 ifeq ifneq
- 无条件分支 goto
- 复合条件分支 tableswitch
…
方法调用指令
- invokevirtual 调用对象实例方法
- invokeinterface 调用接口
- invokespecial 调用特殊处理方法 实例化方法,初始化方法
异常处理指令
- athrow
同步指令
- 同步结构都是由管程(Monitor)来支持的
- synchronized 由monitorenter和monitorexit两个指令来实现的。
类加载机制
加载 -> 连接 -> 初始化 -> 使用 -> 卸载
连接: 验证 准备 解析
https://www.cnblogs.com/xlyslr/p/5751039.html
类加载的过程
加载
- 通过一个类的全限定名来获取定义此类的二进制流
- 将这个字节流代表的静态存储结构转换化成方法区的运行时的数据结构
- 将内存中生成的一个代表这个类的Class对象,作为这个类的各种数据的访问入口
验证
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
准备
准备阶段正式为类变量分配内存并设置变量的初始值。这些变量使用的内存都将在方法区中进行分配。
解析
- 虚拟机将常量池中的符号引用替换成直接引用的过程
符号引用
符号引用是一个字符串,它给出了被引用的内容的名字并且可能会包含一些其他关于这个被引用项的信息——这些信息必须足以唯一的识别一个类、字段、方法。这样,对于其他类的符号引用必须给出类的全名。对于其他类的字段,必须给出类名、字段名以及字段描述符。对于其他类的方法的引用必须给出类名、方法名以及方法的描述符。
直接引用
- 直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)
- 相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
- 一个能间接定位到目标的句柄
解析分类:
- 类和接口的解析
- 字段解析
- 类方法解析
初始化
类加载的最后一步
执行类构造器()方法执行的过程
** 重点过程
类加载的时机
- 初始化
- 当遇到new/getstatic/putstatic/invokestatic 这4条字节码指令时,如果类没有进行初始化过,则需要先触发其初始化;
- 使用Java.lang.reflect包的方法对类进行反射调用时候,如果类没有进行初始化,则需要先触发其初始化;
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化;
- 当虚拟机启动时,用户需要指定一个要执行的主类(main()方法),虚拟机会先初始化这个主类
- 不被初始化的情况
- 通过子类引用父类的静态字段,子类不会被初始化
- 通过数组定义来引用类
- 调用类的常量
类加载器
- 启动类加载器
- 扩展类加载器
- 应用程序类加载器
自定义类加载器
双亲委派模型 jdk1.2之后出现
虚拟机字节码执行引擎
JVM 内存基础概念之程序计数器与 Java 虚拟机栈和本地方法栈
http://blog.csdn.net/airsaid/article/details/50619638
深入JVM字节码执行引擎
http://blog.csdn.net/dd864140130/article/details/49515403
运行时栈帧结构
局部变量表
- slot变量槽 32位
- byte boolean short char int float double long reference
- long与double占俩相邻slot,其它类型占一个slot。
- 当一个变量的PC寄存器的值大于作用域时,slot是可以复用的。
操作数栈
- 动态链接
方法返回地址
- 方法调用时通过一个指向方法的指针指向方法的地址,方法返回时将回归到调用处,哪个地方是返回地址
附加信息
方法调用
http://blog.csdn.net/sunxianghuang/article/details/52280002
方法调用并不等于方法执行,方法调用阶段的唯一任务就是确定被调用方法的版本解析
- invokestatic 静态方法
- invokespecial 构造器方法和私有方法等等
- invokevirtual
- invokeinterface
- invokedynamic
- 编译阶段就能确定下来的方法版本 静态方法 私有方法等等。
分派
静态分派
- 方法的重载
- 静态类型分派的
- 编译阶段已经确定
- 向下转化
动态分派
- 方法的重写
- 运行时期决定的
动态语言支持