我看 JAVA 之 JVM
概念
JVM,顾名思义是一个对物理机器的抽象软件实现,正是因为有了 JVM 的存在,java 语言程序才可以跨平台(Write Once Run Anywhere)。
架构图

JVM 工作机制
主要组成
类加载器
运行时数据区
执行引擎
类加载器
java 动态类加载机制是通过 java 类加载器子系统实现的。它负责对.class 文件进行加载,链接,以及初始化(初始化发生在运行时第一次被应用的时候,而非编译阶段)。
加载
类加载器负责类的加载,主要包含 BootStrap 类加载器,Extension 类加载器和 Application 加载器。
BootStrap 类加载器 —— 是 java 类加载层次中最顶层的类加载器,负责加载 JDK 中的核心类库,如:rt.jar、resources.jar、charsets.jar 等
。这个加载器的优先级最高。
Extension 类加载器 —— 负责加载扩展文件夹(/jre/lib/ext/)中的类。
Application 类加载器 —— 负责加载应用级 classpath 和环境变量指向的路径下的类。
JVM 通过双亲委派模型进行类的加载,当然我们也可以通过继承 java.lang.ClassLoader 实现自定义的类加载器。
链接
校验 —— 字节码验证器校验输入的字节码格式是否正确。
准备 —— 对于所有的静态变量申请内存并分配默认值。
解析 —— 将常量池中的符号引用替换为直接引用的过程。符号引用就是 class 文件中 CONSTANT_Class_info、CONSTANT_Field_info、CONSTANT_Method_info 等常量
初始化
本阶段所有的静态变量会被赋原始默值,并且静态代码块会被执行。
运行时数据区
方法区
JVM 包含一个所有 JVM 线程共享的方法区,存储的数据是非线程安全。主要用来存放类的元数据信息(类的方法代码、变量名、方法名、访问权限,返回值等)。
HotSpot 实现:方法区是一个规范,不同 jvm 有不同的实现。java8 之前,方法区放在堆内存的永久带。java8 取消永久区,方法区放在元空间(本地内存,即堆外内存)。

堆
堆是一个共享资源,存储的数据是非线程安全。所有对象及其对应的实例变量和数组等存储在这个区域。注:堆可以单独讲一讲。
栈
每一个线程执行,JVM 都会为其分配一个独立内存空间,称为虚拟机栈。
每个栈由多个栈帧(Frame)组成,对应每次方法调用所占用的内存。
每个线程只有一个活动栈帧,对应当前正在执行的方法。
PC 寄存器(程序计数器)
程序计数器用来保存执行指令的地址,一旦指令被执行,程序计数器会更新为下一条指令。属于线程私有的数据区域,是一小块内存空间,主要代表当前线程所执行的字节码行号指示器。
本地方法栈
本地方法栈属于线程私有的数据区域,主要与虚拟机用到的 Native 方法相关,一般我们无需关心此区域。
执行引擎
解释器
当 Java 虚拟机启动时会根据预定义对字节码采用逐行解释的方式执行,将每条字节码文件中的内容“解释”为对应平台的本地机器指令执行。
优缺点:响应速度快,但是总体效率较低。
JIT
JIT 编译,全称 just-in-time compilation,按照其原始的、严格的定义,是每当一部分代码准备要第一次执行的时候,将这部分代码编译并缓存,然后跳进编译好的代码里执行。
优缺点:响应速度稍慢,寻找热点代码优化、总体效率很高。
注:Java 语言中解释器与编译器并存,既是 Java 语言的特色,也是 Java 语言的优势。
如何触发 JIT?
寻找热点代码,使用方法调用计数器来统计方法被调用的次数,其默认阈值在 Client 模式下是 1500 次,在 Service 模式下是 10000 次。超过这个阈值 JIT 就会被触发,把编译找到的热点代码缓存在元空间中,效率会大大提高。
目前,jdk8 64 位的机器默认就是使用的 Service 模式、JIT 编译和解释器执行混合模式。
可以通过虚拟机参数 -XX:CompileThreshold 手动设定。
执行命令:java -XX:+PrintFlagsFinal -version ,可以查看 jvm 默认参数值。
GC
随着程序的运行,内存中对象、变量等占据的内存越来越多,如果不及时进行垃圾回收,必然会带来程序性能的下降,甚至造成一些不必要的系统异常(比如 OOM)。
通过引用计数法和可达性分析来判断是否有必要进行垃圾回收。
常见的回收算法:标记清除、复制算法、标记-整理算法、分代回收算法、分区回收算法。
HotSpot 虚拟机中的主要包含如下几种垃圾收集器:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1、ZGC。
注:后续可以单独开一章节介绍 GC 相关。
参考资料
https://docs.oracle.com/javase/specs/jls/se11/html/index.html
https://docs.oracle.com/javase/specs/jvms/se11/html/index.html
评论