1. 首先了解java项目运行流程
我们写好的的java程序,一般是
.java
文件,在我们进行运行时,.java
文件首先会被javac
编译成.class
文件,也就是将程序文件编译成二进制字节文件
,然后这个字节文件会由JVM加载执行
,执行时,解析器会把字节码信息解释成操作系统能识别的指令码
,操作系统把解释器解析出来的指令码,调用系统的硬件执行最终的程序指令。
流程大致是:
java文件——>javac——>class文件——>JVM——>指令码
2. JVM是什么
JVM
的中文名称叫Java虚拟机
,它是由软件技术模拟出计算机运行的一个虚拟的计算机。
JVM
也充当着一个翻译官的角色,我们编写出的Java程序,是不能够被操作系统所直接识别的,这时候JVM的作用就体现出来了,它负责把我们的程序翻译给系统“听”,告诉它我们的程序需要做什么操作。
3. JDK、JRE、JVM的关系
JDK:
是Java开发工具包,它是程序开发者用来编译、调试Java程序,它也是Java程序,也需要JRE才能运行。JRE:
是Java运行环境,所有的Java程序都要在JRE下才能运行。JVM:
是Java虚拟机,它是JRE的一部分,一个虚构出来的计算机,它支持跨平台。
3. JVM的内部体系结构
JVM的体系结构:
类加载器
:加载class文件运行时数据区
:用于保存Java程序运行过程中需要用到的数据和相关信息执行引擎
:用来执行Java字节码
3.1 类加载器
3.1.1 什么叫做类加载
类加载简单的说就是JVM通过
类加载器ClassLoader
,把.class
文件中的信息,拼装成Class
对象放入内存中。
.java——>编译——>.class——>ClassLoader——>Class
3.1.2 类加载的工作过程
类加载得工作工程分为三步:
加载、连接、初始化
,其中连接过程又分为三步:验证、准备、解析
类加载的工作过程如图所示:
(1)加载
- 通过类的全限定名(包名 + 类名),获取到该类的.class文件的二进制字节流
- 将二进制字节流所代表的静态存储结构,转化为方法区运行时的数据结构
- 在内存中生成一个代表该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
加载的流程可以总结为:加载二进制数据到内存
—> 映射成jvm能识别的结构
—> 在内存中生成class文件
(2)连接
连接是指将上面创建好的class类合并至Java虚拟机中,使之能够执行的过程
① 验证
确保加载的类信息符合JVM规范,没有安全方面的问题。主要验证是否符合Class文件格式规范,并且是否能被当前的虚拟机加载处理。
② 准备
为类中的静态字段分配内存,并设置默认的初始值
③ 解析
Java虚拟机将常量池内的符号引用替换为直接引用的过程
(3)初始化
初始化是为类的静态变量赋予正确的初始值
,准备阶段和初始化阶段看似有点矛盾,其实是不矛盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析(后面在说),到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。
3.1.3 类加载器的分类
类加载机制(双亲委派):
工作过程:如果一个类加载器接收到类加载的请求,它会
先把这个请求委派给父加载器去完成
,只有当父加载器反馈自己无法完成加载请求时,子加载器才会尝试自己去加载。可以得知,所有的加载请求最终都会传送到启动类加载器中
总结:Bootstrap Classloader ——> Extension ClassLoader ——> Application Classloader
- 第一个:启动类/引导类:Bootstrap ClassLoader
这个类加载器使用C/C++语言实现的,嵌套在JVM内部,java程序无法直接操作这个类
。它用来加载Java核心类库
,用于提供jvm运行所需的包。并不是继承自java.lang.ClassLoader,它没有父类加载器,它加载扩展类加载器和应用程序类加载器,并成为他们的父类加载器
,出于安全考虑,启动类只加载包名为:java、javax、sun开头的类
- 第二个:扩展类加载器:Extension ClassLoader
主要负责加载Java的扩展类库
,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D,java.ext.dirs选项指定的目录。
- 第三个:应用程序类加载器:Application ClassLoader
负责加载环境变量classpath或者系统属性java.class.path指定路径下的类库
,它是程序中默认的类加载器,我们Java程序中的类,都是由它加载完成的
。
- 第四个:自定义类加载器:User ClassLoader
负责加载程序员指定的特殊目录下的字节码文件的
。大多数情况下,自定义类加载器只需要继承ClassLoader这个抽象类
,重写findClass()和loadClass()两个方法即可。
3.2 运行时数据区
运行时数据区,如下图所示:
方法区:
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区是很重要的系统资源,是硬盘和CPU的中间桥梁,承载着操作系统和应用程序的实时运行。
堆:
Java虚拟机所管理的内存中最大的一块,唯一的目的是存放对象实例。由于是垃圾收集器管理的主要区域,因此有时候也被称作GC堆
程序计数器:
当前线程所执行字节码的行号指示器。每一个线程都有一个独立的程序计数器,线程的阻塞、恢复、挂起等一系列操作都需要程序计数器的参与,因此必须是线程私有的
本地方法栈:
与虚拟机的作用是相似的,只不过虚拟机栈是服务Java方法的,而本地方法栈为虚拟机调用Native(本地)方法服务的
虚拟机栈:
早期也叫Java栈, 每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应这个一次次的java方法调用。它是线程私有的
3.3 执行引擎
执行引擎的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。简单来说,JVM中的执行引擎充当了将高级语言翻译为机器语言的译者.里面包括解释器、及时编译器、垃圾回收器,是Java虚拟机核心的组成部分之一
执行引擎的工作流程:
执行引擎在执行的过程中究竟需要执行什么样的字节码指令完全依赖于PC寄存器。
每当执行完一项指令操作后,PC寄存器就会更新下一条需要被执行的指令地址。
当然方法在执行的过程中,执行引擎有可能会通过存储在局部变量表中的对象引用准确定位到存储在Java堆区中的对象实例信息,以及通过对象头中的元数据指针定位到目标对象的类型信息。
java代码编译和执行的过程
解释器:
当Java虚拟机启动时会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容“翻译”为对应平台的本地机器指令执行。
JIT(Just In Time Compiler)编译器:
就是虚拟机将源代码直接编译成和本地机器平台相关的机器语言。
热点代码
:某个方法或代码块运行特别频繁的时候