Java基础知识专题3-Java代码运行原理

Java基础知识专题3-Java代码运行原理

前言

作为一名Java开发者,JVM是我们每天都要使用的东西,对其有一定的了解于我们的工作有莫大的好处。尤其是当我们遇到内存溢出、程序性能差或CPU满载等这类非常棘手的问题时,一名对JVM非常了解的开发者往往能够事半功倍的解决它们。

JVM并不是只有一种,很多厂家都有自己的JVM,但是使用最广泛的是基于Sun的HotSpot模式(热点代码模式)开发的各类JVM。当然我们指的JVM一般都是OracleJDK中的JVM。

Java程序运行的基本过程

Java代码从编写完成,到计算机运行这些代码的整体过程如下如:
Java代码运行过程
从上图中可以看出,如我们上一章讲到的:编译过程我们依赖JDK、运行过程我们依赖JRE,最终我们的代码通过JVM得以在操作系统上运行。

下面我们就根据这个流程的每个步骤弄明白程序到底是怎么从编写完成到运行的。

开发完成准备执行:Java源代码编译

之前我们提过,Java编程语言是高级语言,语法贴近自然语言,人类易懂但是机器看不懂,所以想要机器读懂运行,就需要翻译成机器语言。但是Java为了方便开发者,在高级语言与机器语言之间加入了JVM,即帮助我们处理了内存、指针等麻烦的问题,同时也实现了跨平台的能力。

所以Java语言第一步的编译并不是直接将我们写的代码转换成机器码,而是先变成JVM能看懂的字节码(.class),然后由JVM将字节码处理成机器码后运行,这就是Java语言中运行程序的第一步源代码编译。

这个过程依赖JDK中的Java源码编译器,即javac,编译过程的处理如下图所示:
编译
由上图可见,源代码编译并不是简单的将代码翻译成JVM字节码,而是要经过一系列的词法分析->语法分析->语义分析,最后生成字节码,通过这个过程让我们的代码更加可靠,最起码不会由于低级失误(如语法、符号等错误)而导致程序无法运行,这也是Java安全和健壮性的体现。

最后生成的.class文件含有以下部分:

  1. 结构信息:包括class文件格式版本号及各部分的数量与大小信息;
  2. 元数据:对应与Java源码中声明与常量的信息,包含类/继承的超类/实现的接口的声明信息、域与方法声明信息和常量池;
  3. 方法信息:对应Java源码中语句和表达式对应的信息。包含字节码、异常处理器表、求值栈与局部变量区大小、求值栈的类型记录、调试符号信息。

当源代码编译成为JVM字节码后,下一步就可以将程序交给JVM进行准备执行了。

JVM执行代码

Java平台结构

在说明JVM是怎么执行代码前,我们先简单介绍一下JVM的结构,以便后面内容的理解。

首先Java作为一种技术,他有一整套内容保证其能够运行解决问题,如下图所示:
Java平台结构图
从上面这张图可以看出整个Java平台的核心则是JVM,它起到了承上启下的关键作用,是应用程序与底层操作系统的“沟通桥梁”。
JVM的上方是Java的基本类库和扩展类库以及他们的API,这些也是我们写代码的时候常用的类库(如Collection等),通过这些类库我们可以方便的实现我们的功能;
它的下方则是移植接口,移植接口由两部分组成:适配器和Java操作系统,适配器是用来适配不同平台的接口、指令等内容,而Java操作系统则是直接对接基于Java的平台。

JVM内部结构

接下来是Java平台的核心JVM的内部结构,如下图:
JVM结构图
从上图看,我们一般把JVM分为三个主要部分,分别是:

  1. 类加载子系统:负责把编译后的.class文件载入到JVM体系中,然后进行初始化操作,把相关的数据放到内存中的指定区域,做具体的内存分配的操作;
  2. 运行时内存区:负责存储程序运行的所需数据,是JVM的核心部分。后续我们会专门用一个章节讲解该部分内容;
  3. 执行引擎:
    • 解释器:运行字节码文件;
    • 编译器:把字节码文件(.class文件)编译成根据当前操作系统能够执行的文件,它决定了Java跨平台特性;
    • 垃圾回收器:会把内存中产生的垃圾进行回收清理,从而释放内存,清理时会根据JVM中垃圾回收算法进行回收,且自动进行回收,如果程序需要调优的话,主要就是调整这里,通过合适的算法进行对JVM的优化处理,避免出现卡顿、内存溢出等情况;
    • Java本地接口(JNI):怎么调用C语言的代码的统一接口;
    • 本地方法库:调用C或C++实现的本地方法的代码返回结果等(与操作系统强关联)。

下面我们根据程序的运行流程,逐一解释每个部分的意义及作用。

第一步:类加载

上面我们完成了将Java源代码编辑成JVM字节码,,那么要执行这些字节码,第一步则是将这些字节码加载到系统中。这是JVM中第一个工具就登场了:

类加载器(ClassLoader),顾名思义它的主要任务就是加载(装载)编译好的class文件中的二进制数据,将读取并放在运行时数据区的方法区内,然后再堆区域创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。

类加载的.class文件来源有以下五种:
1、本地磁盘;
2、网上下载(Applet);
3、从数据库中;
4、压缩文件中(zar、jar等);
5、从其他特殊类型文件(jsp);

类加载的过程并不简单,由于Java程序有很多内置的API、还有我们自定义的内容、什么时候该加载哪些内容?用完怎么办?等一些列问题,所以类加载后续我也会使用单独的章节进行讲解,方便理解。

第二步:分配内存、创建对象

类加载器将字节码文件加载到内存中后,会根据不同的内容将要执行的代码分配到不同的内存区块中、同时为各个类创建java.lang.Class对象。主要分为下面五个部分:

  1. 方法区:方法区用来存储被加载的class文件的相关信息,当虚拟机装载一个class文件后,它会从这个class文件包含的二进制数据中解析类型信息,然后将该相关信息存储到方法区中;
  2. 堆:堆是用来存储相关应用类型的,如new出来的对象。当程序运行时,虚拟机会把所有该程序在运行时创建的对象放在堆中;
  3. PC寄存器:PC寄存器主要用来存储线程。当新创建一个线程时,该线程都将得到一个自己的PC寄存器(程序计数器)以及一个java栈。Java虚拟机没有寄存器,其指令集使用Java栈来存储中间数据;
  4. 栈区:栈区主要用来存储值类型的,如基本数据类型。java栈是由许多栈帧组成的,一个栈帧包含一个Java方法调用状态,当线程调用一个方法时,虚拟机压入一个新的栈帧到该线程的Java栈帧中,当该方法返回时,这个栈帧从java栈中弹出。

概括一下就是:方法区用来存要执行的代码,堆用来存运行过程中产生的对象,PC寄存器和栈则为程序运行过程中的线程服务,存储中间数据。
由于JVM内存关系到我们代码的调优和根本问题,所以后面我也会单独用一个章节来细讲JVM内存。

第三步:执行引擎执行

所有的准备工作都就绪了,那么接下来就要执行引擎来运行我们的代码了!

首先对于JVM来说执行引擎是包含着多个工具的一个解决方案。执行引擎的任务就是根据输入的字节码文件,解析字节码然后根据字节码中的指令进行操作,最终返回执行结果。

字节码文件通过类装载器装载,被分配被分配到JVM的运行时数据区,然后会被执行引擎执行。

执行引擎以指令为单位读取Java字节码。它就像一个CPU一样,一条一条地执行机器指令。每个字节码指令都由一个1字节的操作码和附加的操作数组成。执行引擎取得一个操作码,然后根据操作数来执行任务,要么交给JIT编译为具体硬件处理器机器码,要么直接由解释器运行。

这个过程需要三个工具支持:

  1. 解释器模式;
  2. JIT编译模式;
  3. 混合模式

执行引擎的执行过程是基于栈的,所以后面章节讲单独举例讲一下。

第四步:JVM垃圾回收(简称GC)

最后,当我们完成了代码逻辑的处理。会在内存中产生很多中间数据、无用对象等。而Java的所有释放都由 GC 来做,GC除了做回收内存之外,另外一个重要的工作就是内存的压缩,这个在其他的语言中也有类似的实现,相比 C++ 不仅好用,而且增加了安全性,当然她也有弊端,比如性能这个大问题。

GC是Java语言基础中的基础,也是最核心的部分之一,所以后续我也会使用单独章节进行详细讲解。

结语

至此,我们对JVM整体的运行流程和结构有了大致的了解,接下来我会将上文中提到的几个核心关键点:

  1. 类加载
  2. JVM内存
  3. 类执行模式
  4. JVM垃圾回收(GC)

逐一用单独的章节来讲解。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值