JVM之Java编译到执行(1)——引(运行java编译器的命令)

createh53个月前 (02-01)技术教程35

Java语言特点

一次编写,到处运行。也就是跨平台。 因为这个跨平台的实现原理,而导致Java的编译流程,与以往的C++之类语言有不同。

各个操作系统的底层实现,资源的协调,和硬件操作各有各的不同。就意味着,如果按照早期使用C++这类语言写,每个系统都有不一样的API,不仅存在大量的API调用区分,而且那就要为每一个操作系统都要编译一次。说来简单,楼主做过这种工作,出bug的时候,会编译很多次,光编译这里,就要消耗不少精力。真的比较烦。

想要做到一次编写到处运行的话,Java采取的方案是,将程序员编写的代码首先优化为一个字节码文件,然后为每一个操作系统提供虚拟机,也就是JVM。 这个虚拟机里可以解释这个字节码文件,并将其生成相应系统可以识别的机器码。所以最简化的路子是下图所示。

这样的话有什么好处呢?

  • 只要字节码文件一样,JVM虚拟机又是专业的人提供的,可以保证上层调用的API是一致的。也可以保证得到良好的解析,最终生成各自系统可识别的机器码,并执行。
  • JVM虚拟机是被人提供的,就意味着,他们对这块的操作权是很大的,可以用顶级的智慧,顶级的开发人员,对各个系统的虚拟机之后进行不断的优化。不用程序员管。
  • 对于语言的支持方面,需要提供良好的API,这就意味着,API的开发操作权也在专业的公司手里。他们足可以按照比较先进的思想,提供比C++好用多得多的API。减少下游程序员的学习难度。


编译过程

Java语言的编译,因为上述的设计方案。会导致编译过程分为两个过程

  • 1 从源代码编译到字节码的环节 (称为前端编译)
  • 2 从字节码到可执行程序的环节 (称为后端编译,运行时做的工作)

其中前端编译,就是那种在计算机中输入一个javac指令,然后就编译成了class文件。在浅层的理解上,认为这就是编译完成了。事实上class文件它不能直接执行的,离执行还差的远。

这些class文件,在真正执行的时候,会让JVM去处理,这个时候属于运行时了已经。在运行时,JVM会拿到已经生成的class文件集,将这些文件里的一条条指令解释为相应平台可以识别的机器码。这样两种配合的情况下,我们写的代码才得以运行。


“解释”

解释型语言简述

解释型语言指的是程序不需要程序员进行编译,程序在运行时才会被翻译成机器码的那类语言。这就意味着,每次执行都得解释一次。因此这类语言有个普遍的缺陷,是效率较低。但是其优势就是跨平台性比较好,我们这里的跨平台烦恼,主要就是编译不同平台所带来的工作工时还有失误的消耗,这个是比较折磨开发者的。解释型是可以解决这种问题的。代表语言为:Python/JavaScript / Perl /Shell 等。 Java也算是。但又没那么彻底。

编译型语言简述

编译型语言的特点是,写的程序执行之前,需要一个专门的编译过程,把程序编译成为机器语言的文件,比如exe文件,以后要运行的话就不用重新翻译了,直接使用编译的结果就行了(exe文件),因为翻译只做了一次,运行时不需要翻译,所以编译型语言的程序执行效率高。缺点就是,开发和维护成本高。其代表语言为 C C++。

解释器VS编译器

上图中有个环节,有一个叫解释器的部分。这块最开始是令我比较迷惑的。原因是我认为Java作为一个解释型语言,那直接解释就好喽。解释不就是编译么?为什么我看到书上,解释和编译要区分呢?为什么这个流程甚至是一边解释一边翻译呢?

原来这里面存在一个区别,解释器和编译器,是实现同一种目标的两种不同的工作方式。

解释器

解释器是直接执行用编程语言编写的指令的程序。它逐行读取源代码,将其转换为机器语言(或某种中间形式),然后立即执行。这意味着解释器在执行程序时,需要逐行解释代码,因此执行速度通常比编译器慢。

编译器

编译器则是将源代码整体转换(或“编译”)为机器语言或低级语言(如汇编语言)的程序。它首先会对源代码进行词法分析、语法分析、语义分析等,然后生成中间代码,并最终转换为机器代码。这个过程通常称为“编译”,生成的机器代码可以独立运行,无需解释器的支持。


前端编译

javac就是Java的前端编译器,是个编译工具,负责把程序员写的java文件,经过初步的分析,转换为class文件。 class文件就是字节码文件。二者可以一块理解。

Java的前端编译基本就是做了这个工作。它的编译流程不做赘述,相当于C++编译或者编译原理的打折版。什么词法,语法,语义分析。这个知识点探下去没完没了,是一个比较深的话题。略过。流程上图已经给出了可以翻上去看一些图片。

Java从编写代码到执行涉及到的编译器概览

我们重点注意的是下半部分,主要由JVM承担的,发生在运行时的后端编译工作!

Java严格的说是一个编译 + 解释混合的语言。这使得我们讨论Java编译到执行这个话题时,不止要讲静态的编译,还要深入了解Java是怎么执行代码的,比如,解释器一条条指令解释,那指令是哪儿来的?存哪里了?类加载环节是不是和这里有关呢?类加载究竟做了什么事?Class除了反射时用,JVM到底用它做什么了?为什么需要它?class文件里面好像有操作指令,这些指令是不是相当于C++可执行程序里的代码段呢?new一个对象JVM划分内存,那个垃圾回收机制是怎么管理这些对象的? 逐渐铺开发现相当于讲1/3JVM机制,一片文章根本讲不完。我会拆分下来一块一块讲。

JVM后端编译

后端编译流程图

对于Java解释字节码很粗略的流程,在上图已经画出来了。总共拆分为两个阶段。

  • 类加载 and 二进制转换
  • 解释器解释 和即时编译器优化同时进行。(为什么这样之后解释)

需求分析

在了解一个方案的解决办法前,有时候思考问题,要把事件重置到还未发生时考虑。有没有想过为什么JVM的后端编译器为什么要把流程搞成这样子?还什么类加载,还要解释器,那个JIT是啥?为什么要有这个?

我们不妨以一个实现者的视角来聊聊。假设你就是这个编译器的实现者

  • 你们的这个大工程中,你负责开发后端编译器这块的组件。你组件的输入内容已经确定了,是前端编译机器的产出--字节码文件,即字节码文件。
  • 你这个组件的产出结果是,生成机器码,能上机跑的那种。(我们排除掉一切更为复杂的需求,如内存回收等等机制)

那你会怎么解决呢?

我想是个人都得先分解输入内容和输出内容这两大关键点的样子吧。 于是乎

  • 1 你必须要从java语言设计的思想,和所有语言通用思想方面来考虑java语言的组成结构。并能做到很好的拆分转换优化。另外,有些代码调用到中级逃不过C语言或C++,他们的接口入口地址,您要怎么找,怎么正确的连接。等等等等一堆的问题。
  • 2 你必须要十分熟悉操作系统原理,尤其是一个程序要跑起来,计算机内部的状态究竟是什么样子。你需要将你输入的那些二进制代码,落实到运行时内存的数据里!
  • 3 锦上添花的其余功能,例如垃圾回收机制。

CPU如何执行程序

粗略的讲。CPU本质上是一个没有主见的, 沿着代码块内存一条一条机械执行的工具, 我们对代码块做了很多的编程, 保证操作指令和被操作数是正确的, 这样cpu识别这些指令,也能一条接一条执行.基本没有错误.

您编译代码的目的就是,把代码变成最终CPU识别的样子。

JVM的思路

JVM的思路就是,模拟这种场景。只不过“代码块内存”里的指令是class文件中描述的字节码指令。JVM模拟CPU的操作流程,也是一条一条往下“读”,像极了CPU执行指令那般。在“读”的过程中,JVM会通过编译器或者解释器,将这些字节码指令转换为真正的机器码。这样就达成了与CPU协议的一致。

JVM的整体流程开始于类加载,我们知道了JVM宏观的思路后,再研究每个环节,下篇文章将先从类加载讲起, 类加载的输入是class文件(字节码文件),楼主将贯彻费曼学习法,知无不言,将他们写出来。

相关文章

一章带你了解Java虚拟机——JVM(揭秘java虚拟机:jvm设计原理与实现)

1、JVM 的体系结构"堆"中存在垃圾而"栈"中不存在垃圾的原因:堆(Heap)用途:堆主要用于存储对象实例和数组。在Java中,几乎所有通过new关键字创建的对象都会存储在堆内存中。内存分配与释放:...

Java虚拟机(jvm)-简介(深入浅出:java虚拟机设计与实现)

一、Java运行时虚拟机内存区域划分1.元空间(Metaspace)元空间(Metaspace)从java8开始替换掉了原来的方法区(Method Area)。相比方法区(Method Area)在元...

JAVA面试题每日一练:描述一下JVM加载class文件的原理机制?

Java是一门高度平台独立的编程语言,但在这个跨平台的特性背后,Java虚拟机(JVM)却承担了一个至关重要的任务:类加载。类加载不仅是Java程序执行的前提,它还是保证Java平台安全性和可扩展性的...

美团架构师探秘Java生态系统,介绍JDK、JVM、JEP

OpenJDKOpenJDK原是Sun MicroSystems公司(下面简称Sun公司)为Java平台构建的Java开发环境,于2009年4月15日由Sun公司正式发布。后来Oracle公司在201...

入门JAVA必须了解的基础知识-JVM(java基础到入门)

JAVA虚拟机(JVM) JVM包含两个子系统和两个组件:类装载系统和执行引擎系统,运行时数据区和本地接口。如同一台真实的机器,有自己的指令集和执行引擎,可以在运行时操控内存区域,可以解读指令代码并与...

深入浅出JVM之如何定位线上JVM各种问题

如何排查java进程内存占用率高找到java进程pidjps -l 运用jps命令找到java进程的pid定位占用内存的大对象jmap -histo:live 12628(注:如果输出内容太多,只想看...