序言
岁月悠悠,衰微只及肌肤;热忱抛却,颓唐必致灵魂
今天来总结一下JVM。
JVM的整体结构
JDK:JVM + 基础类库 + 编译工具;
JRE:JVM + 基础类库;
说明:
- 线程私有:虚拟机栈,本地方法栈,程序计数器;
- 线程共享:堆,方法区;
- 执行引擎:解释器(Interpreter)、JIT(即时编译器,编译器后端)、GC(垃圾回收器);
- JVM直接和操作系统对话;
- 先经过类加载器,类信息放在方法区中,类的实例对象放在堆中;
- 解释器逐行解释代码,翻译成机器码;
栈是属于线程的,堆是属于进程的
此外,stack创建的时候,大小是确定的,数据超过这个大小,就发生stack overflow错误,而heap的大小是不确定的,需要的话可以不断增加。
Java代码执行流程
JVM的架构模型
JVM的生命周期
说明:
jps
查看JVM进程
类加载子系统
加载:
讲图:
instanceKlass相当于一种数据结构;
堆中存放Person.class的类对象,这个类对象有instanceKlass的内存地址
instanceKlass也存放着Person类对象的内存地址;
对于Person的实例化对象来说,对象头部存放Person类对象地址;
那么就会先去找class类对象,再间接去元空间去找instanceKlass对象,再之后就会去元空间里面找_Methods,_fields
,去调用对象方法。
链接:
验证:
文件格式验证:0xCAFEBABE
元数据验证:对字节码描述的信息进行语义分析
字节码验证:最复杂
通过数据流分析和控制流分析,确定程序是合法的,符合逻辑的
符号饮用验证:确保解析行为可以正常运行
准备:
- 对于final,static:
- 基本类型会直接给值
- 包装类就会先0再赋值
- 在JDK7之后,静态变量的内存不再方法区内,会跟着类对象存储在堆中。
- 常量也会在准备阶段被赋值;
解析:其实就是
#1->内存指针
的过程;初始化:
讲图:
()就是给类变量赋值; 类一旦被初始化,那么static{}代码块就会执行;
类加载器分类
启动类加载器
扩展类加载器
应用程序类加载器
用户自定义类加载器
关于ClassLoader
双亲委派机制
沙箱安全机制
类的主动使用和被动使用
运行时数据区
程序计数器
说明:
并行是时刻,并发是时间段;
并行是同时执行,并发是一个时间片内交替执行;
虚拟机栈
说明:
栈空间也可以存放堆中对象的引用值;
栈:每个【线程】运行时需要的内存空间,每个线程只能有一个活动栈桢;
栈桢:每个【方法】运行时需要的内存空间:
局部变量表/操作数栈/动态链接/返回地址
活动栈桢:对应着当前正在执行的方法,往往在栈顶部;
局部变量表
说明:静态方法是不允许使用this关键字的
操作数栈
它是由数组实现的
Demo
栈顶缓存技术
动态链接
运行时常量池在方法区中;
方法的调用
虚方法和非虚方法
this/super基本上都是非虚方法,在编译期间就可以确定
invokestatic/invokespecial
如果隐式调用父类的final方法(也就是不加super.),是invokevirtual
但如果super.的方式,则是invokespecial
子类重写也是invokevirtual