Java 运行机制

导读

  1. JVM 是什么
  2. Java 程序执行流程
  3. Java 类加载器机制
  4. JIT 工作机制

JVM 是什么?

JVM(Java Virtual Machine) 是模拟执行某种指令集体系结构(ISA)的软件,是对操作系统和硬件的一种抽象(官方文档定义)。

在硬件和操作系统独立性的技术实现方面,他实现了以下两个方面的功能:

  • 编译代码,使其占用的空间更小;
  • 保护用户免受恶意代码的攻击;

HotSpot JVM 演化过程

SUM HotSpot VM -> Oracle JRockit JVM -> Oracle HotSpot JVM

Java程序的执行流程

Java 程序被编译成为 .class 的字节码,并加载到 JVM 中执行。

通过 Java 的运行流程可知。JVM 是程序的运行的基础,通过使用不同平台的 JVM 解决了平台的差异性问题。

流程概述

1
2
3
4
5
编写 .java 源文件,并编译成 .class 字节码;
通过 JVM 类加载机制将 .class 类加载到 JVM;
JVM 解释 .class 字节码为机器码;
执行解释后的机器码;
程序运行完成,卸载类;

JVM 的生命周期

1
当启动一个Java程序时,一个虚拟机实例就诞生了。程序关闭或者退出,实例也随之结束。

JVM 是如何工作的?

JVM分为三个子系统

1
2
3
类加载器子系统
运行时数据区
执行引擎

类加载子系统

Java的动态类加载功能由类加载器子系统处理。在运行时首次引用类时初始化类文件。类的加载是将 .class 文件加载到 JVM 的过程。

  • 两种触发类加载方式(显示、隐式):
1
2
3
4
5
// 显示加载
Object obj = new Object();
// 隐示(动态加载)
Class clasz = Class.forName(someClassName);
Object obj = clasz.newInstance();

加载(loading)

Heap 空间生成这个类的对象,方法区生成该类的描述信息,作为这个类的各种数据入口。

多种类加载器

1
2
3
4
5
6
7
8
9
10
11
引导类加载(Bootstrap) 
加载JAVA_HOME/lib目录中,被JVM认可的类(-Xbootclasspath可重新指定目录)。

扩展类加载(Extension)
加载JAVA_HOME/lib/ext目录中,被JVM认可的类(java.ext.dirs重新指定目录)。

应用类加载(Application)
加载用户指定的classpath目录上的类。

用户自定义类加载(User-Defined)
用户继承java.lang.ClassLoader,自定义类加载器。

JVM 使用双亲委派机制进行类的加载。

双亲委派机制:当一个类收到类加载任务,会先委托父类加载器去完成,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。

链接(linking)

1
2
3
4
5
6
7
8
验证
确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

准备
在方法区中分配类变量所使用的内存空间。

解析
所有符号内存引用将被来自方法区域的原始引用所替换。

初始化(initialzation)

初始化过程中,所有静态变量都将被赋初始值,并且静态块和构造器方法也会被执行。
以下几种情况不会执行类初始化

1
2
3
4
5
6
通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
定义对象数组,不会触发该类的初始化。
常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。
通过类名获取Class对象,不会触发类的初始化。
通过Class.forName加载指定类时,如果指定参数initialize为false时,也不会触发类初始化。
通过ClassLoader默认的loadClass方法,也不会触发初始化动作。

运行时数据区

JVM 运行时的内存主要有五个部分,其中堆和方法区是线程共享的数据区,而程序计数器、Java虚拟机栈和本地方法栈是线程私有的数据区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
1. 线程共享数据区

堆(Heap)

堆内存生命周期与 JVM 相同,在线程间共享访问。
主要存放对象或数组实例。是 GC 的主要对象。
当 JVM 申请不到足够内存时,会抛出 OutOfMemoryException。

配置参数:-Xms512M -Xmn2048M

由于线程共享堆内存,所以在线程安全上主要从两个方面去解决:
a. 加锁互斥访问;
b. 线程本地分配缓冲区(ThreadLocal);

方法区(Method Area)

方法区生命周期与 JVM 相同,被多个线程共享。
主要存放类信息和运行时常量池,每个加载的类都会在方法区形成一个与之对应的类信息的数据结构。
包括类名、直接超类、实现的接口列表等。

当 JVM 申请不到足够内存时,会抛出 OutOfMemoryException。


2. 线程私有数据区

程序计数器(Program Counter Register)

程序计数器生命周期与线程相同,是线程私有的。
若当前正在执行 Java 方法,程序计数器中存放下一条字节码指令的地址。
程序计数器是唯一一块没有规定会抛异常的区域。

Java 虚拟机栈(Java Virtual Mathine Stacks)

Java 虚拟机栈的生命周期与线程相同,是线程私有的。
主要存放栈帧(Frame),用于保存方法调用和返回,存储局部变量和中间结果。

配置参数:-Xss320KB

可能会有两种异常:
a. StackOverFlowException(栈的深度大于一定的阀值)
b. OutOfMemoeryException(申请不到足够的内存)

本地方法栈(Native Method Stacks)

本地方法栈生命周期与线程相同,是线程私有的。
于支持本地方法的调用,也叫做 `C Stack`。
允许固定大小,或者根据计算需要动态扩容。

可能会有两种异常:
a. StackOverFlowException(栈的深度大于一定的阀值)
b. OutOfMemoeryException(动态扩容下,如果申请不到足够的内存)

JVM详细配置参数

执行引擎

被分配给运行时数据区的字节码将由执行引擎执行。执行引擎读取字节码并逐个执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
解释器
解释器更快地解释字节码,但执行速度很慢。
解释器的缺点是,当一个方法被多次调用时,每次都需要一个新的解释。

JIT编译器
JIT编译器消除了解释器的缺点。
执行引擎将在转换字节码时使用解释器的帮助,但是当它发现重复的代码时,它使用JIT编译器,JIT编译整个字节码并将其更改为本机代码。
此本机代码将直接用于重复的方法调用,从而提高系统的性能。

中间代码生成器
生成中间代码

代码优化器
负责优化上面生成的中间代码

目标代码生成器
负责生成机器代码或本地代码

分析器
一个特殊的组件,负责寻找热点,即方法是否被多次调用。

垃圾收集器
收集和删除未引用的对象。
可以通过调用 System.gc()触发垃圾收集,但不能保证执行。
JVM的垃圾收集收集创建的对象。

JIT 工作机制

JIT (Just In Time) compiler 及时编译器,在运行时,JIT会把翻译后的机器码保存起来,以备下次使用。工作在 .class 字节码到机器码转换的过程。

工作原理:

所以 JIT 是通过减少重复翻译机器码来提升性能的。JIT 编译器在运行程序时有两种编译模式可以选择,并且其会在运行时决定使用哪一种以达到最优性能。(这样已经能解决大部分问题,在 JVM 调优中,相对而言需要做的比较少)

1
2
3
4
JVM Server 模式
由于采用了相对重量级代号为 C2 的编译器,速度较慢,但是一旦运行起来后,性能将会有很大的提升;
JVM Client 模式
由于采用了轻量的编译器 C1,启动速度快。

了解 JIT 配置参数

总结

通过 Java 程序的运行流程,了解到了 JVM 在 Java 运行中的作用。另外摘录了 JVM 相关的知识点,辅助和加强对 JVM 体系结构的认识。本文仅仅是笔记的整理,详细的知识点,还请参考下面的文档。

参考文档

https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
https://www.dynatrace.com/resources/ebooks/javabook/how-garbage-collection-works
http://www.oracle.com/technetwork/java/javase/tech/exactoptions-jsp-141536.html
http://openjdk.java.net/jeps/122
https://javapapers.com/java/java-garbage-collection-introduction
https://javapapers.com/java/how-java-garbage-collection-works
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html
https://docs.oracle.com/javase/10/gctuning/factors-affecting-garbage-collection-performance.htm
https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/index.html
https://javainterviewpoint.com/category/java/
https://www.cnblogs.com/liululee/archive/2019/09/04/11461998.html
https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-2.html
https://docs.oracle.com/javase/10/gctuning/toc.htm

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2015-2020 Andrew
  • Powered by Hexo Theme Ayer
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信