image frame

无论走到哪里,
都应该记住,
过去都是假的,
回忆是一条没有尽头的路。
一切以往的春天都不复存在,
就连那最坚韧而又狂乱的爱情,
归根结底也不过是转瞬即逝的现实,
唯有孤独永恒。

——加西亚·马尔克斯

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

  • © 2015-2020 Andrew
  • Powered by Hexo Theme Ayer
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信