java执行class找不到main函数_你所不知道的HelloWorld背后的执行原理

专注于Java领域优质技术,欢迎关注

作者:饭谈编程

【今日最佳】对于程序员而言,所谓的二八定律指的是 花百分之八十的时间去学习日常研发中不常见的那百分之二十的原理。

据说阿里某程序员对书法十分感兴趣,退休后决定在这方面有所建树。于是花重金购买了上等的文房四宝。

一日,饭后突生雅兴,一番磨墨拟纸,并点上了上好的檀香,颇有王羲之风范,又具颜真卿气势,定神片刻,泼墨挥毫,郑重地写下一行字:hello world。

6cd635a58befde380ebf2666ef006a28.png

当然了,这是个专属程序员的段子哈哈哈。

那么问题来了,写了这么久的Hello World,大家确定自己了解自己写的东西背后是什么原理吗?(o≖◡≖)

【给出2分钟,该知识点涉及到了Java程序执行流程,包括编译、加载和执行,你是否能够理清呢?】

接下来进入严肃时间 (@ ̄ー ̄@)

与众不同的Hello World

c6e38da02ad74865a77aed9c2f136d3c.png

整个代码的执行过程可以分为三个阶段:

  • 代码编译
  • 类加载
  • 类执行

代码编译

代码编译的作用就是将我们编写的 Main.java文件转化为Main.class文件,.class在这里又被称为字节码文件,打开就是一堆的火星文【反正就是看不懂】,在这里我们可以将编译的过程看作生产JVM原料的过程,使用的工具就是jdk提供的工具javac。 大致流程如下:

  • 词法分析,即将源代码的字符流转变为Token集的过程。白话文描述下,就是在我们实际编程中,单个字符是最小单位,而实际上在编程过程中,标记才是最小单位,如关键字、变量名、字面量、运算符等都可以成为Token,貌似还是有点蒙蔽,举个例子(>﹏<),比如整型int在我们编程中它就是三个字符组成的,就是i、n、t 三个字符,而在编译过程中它就是一个Token,不可拆分。

这个过程对我们来说其实是完全屏蔽的,但是实际上它是现代经典编译原理的 套路,词法分析也是为了给后面编译做准备的】

  • 语法分析,通过词法分析拿到Token集后,下一步就是构建抽象语法树了,所谓的抽象语法树其实就是一种用来描述程序代码语法结构的树形表示方式,其中语法树的每一个节点都代表着程序代码中的一个语法结构,如包、类型、修饰符、运算符等。

在我们眼中,Main.java已经可以清晰理解到底写的是什么东西了,但是对于JVM来说还是一脸懵逼的,所以才需要构建成语法树,在这一步后就不会再对源码文件进行操作了,后续的操作都建立在抽象语法树上

  • 填充符号表,符号表是由一组符号地址和符号信息构成的表格,这个表格在编译的不同阶段都会被用到,如在目标代码生成阶段,会对符号名进行地址分配,而符号表就是地址分配的依据。
  • 语义分析,语义分析阶段也可以说是语义检测阶段,上面说到语法分析会构建一棵语法树,那么这棵语法树是否是正确合理的,就由语义分析来做了,语义分析会通过标注检查和数据及控制流分析检查两步入手,在生成字节码的最后一步信息把关。
  • 字节码生成,这是javac编译过程的最后一个阶段了,字节码生成阶段并不只是简简单单的将前面各个步骤生成的信息转化成字节码然后放入磁盘中,还进行了少量的代码添加和转换工作,如我们自己重载的构造函数。

编译期到这里就结束了,那么由谁来将这些原料传输给JVM虚拟机呢?这个时候就要看看类加载的过程了。

类加载

类加载简单来说就是将由类加载器将编译后的字节码文件【Main.class】加载到虚拟机中 ,那么自然而然的,要先介绍下四种类加载器

说说四种类加载器

3656f0094d20ddc86211c9e5134e219f.png

可以从上图中看出,类加载器可以分为四种,而第四种是由我们自己实现的,那么其他三种由JVM提供的类加载在我们启动该Main程序的过程中起到了什么作用呢?

首先说说启动类加载器 Bootstrap ClassLoader ,启动类加载器的作用主要是加载 %JAVA_HOME%jrelib.jar 类库,将其加载到虚拟机内存中,那么rt.jar类库到底有什么作用呢?rt.jar下包含了Java的基础类库,也就是Java doc里面看到的所有的类的class文件,感兴趣的朋友可以自己打开目录看下。

其次是扩展类加载器 Extension ClassLoader ,扩展类加载器的作用主要是负责加载JAVA_HOMEjrelibext目录下的所有类库,主要是载入扩展包。

再者是系统类加载器 Application ClassLoader, 也称之为应用程序类加载器,负责加载用户类路径(也就是我们配置的CLASSPATH)上所指定的类库,是应用程序中默认的类加载。

看完以上三个类加载器的简单描述过程,是不是有种终于知道了我们配置的jdk环境的最终作用了吧,是的,就是让类加载器识别到后加载各种类库。

那么问题来了?是哪个类加载器加载了我们的Hello World程序呢?是的,就是应用程序中默认的类加载器 Application ClassLoader。

知道了类加载器后,那接下来总要了解下类加载器怎么加载的吧?

说说类加载的过程

422137b3b8aced2d306d3c370d568d30.png

网上找了张图片,简单明了。虽说是简单明了,不过确实异常重要的,因为是面试热点(✿◡‿◡)

加载

其实就是上文说到的系统类加载器 Application ClassLoader将编译后的Main.class文件加载到内存中。

【思考】抛出个问题,所谓的加载到内存中,我们都知道JVM把内存分成了几大模块,那么请问是加载到哪个模块中?热点面试题,答案见文末

链接

链接中包含了三部曲,总的作用就是负责将Main.class的二进制数据合并到JRE中。

关于三部曲,其实很好理解;

首先是验证阶段,类加载器将二进制字节流加载到虚拟机中,肯定是需要进行验证的,避免危害虚拟机自身安全,而这也是验证阶段存在的价值;

接下来是准备阶段,准备阶段是正式为类变量分配内存并且设置类变量默认值的地方,比如上面HelloWorld程序中的

private static String word = "Hello World!";

注意我描述的第一个是类变量,也就是static所描述的变量,其次是默认值,也就是上面的word的默认值null,如果是数字则为0。

最后是解析阶段,解析阶段的作用主要是将常量池内的符号引用替换为直接引用的过程,解析阶段其实有点难理解,至少是比上面的两个阶段要难理解的,我这里尽量直白点;

所谓的符号引用指的是包含了类的信息、方法名、方法参数等信息的字符串,而当第一次运行时,JVM会根据这行字符串去检索到对应的方法入口,而为了下次不用再做同样的检索,在第一次运行的时候就会将符号引用替换成直接引用,这样后面就可以省去一定的消耗了;这里的直接引用其实就是指偏移量,虚拟机可以通过偏移量直接找到方法入口,不再需要做检索了。

初始化 终于来到初始化阶段了,上面我们有说到word默认值是null,是系统赋的默认值,而在初始化阶段,则是根据我们人为的初始化类变量和其他资源,比如上面的word则被我初始化成了"Hello World!"。

类执行

上面说到Main.class被加载到了Java虚拟机内存中,那么接下来便是执行的过程了。那么由谁来执行这一过程呢? 如图

a14f3715f99370e7adfed67f74f0834e.png
  • 实际上,一个Java虚拟机在运行的时候可以划分为三个子系统:类加载子系统
  • 执行引擎子系统
  • 垃圾收集子系统

很明显、很清晰,图中的类加载子系统在上面已经谈了,执行引擎子系统就是负责执行这一部分的,那么过程是怎么样的呢?

其实很简单,执行引擎会把字节码转换为机器码【what?竟然还要转换。拜托,字节码是被JVM识别的语言,机器码才是最终被操作系统识别的语言】

然后操作系统才可以真正调用,很多学或者做Java的人都听过JIT,但是都不知道具体是干嘛的,没错说的就是你。

这里终于可以解释下了,字节码转换成机器码的翻译工作使用的就是JIT(Just In Time)即时编译器(对热代码整段编译)和Java字节码解释器(一行一行解释字节码)来完成的。 这里给下JIT编译的工作流程:

JVM字节码 -> 机器无关优化 -> 中间代码 -> 机器相关优化 -> 中间代码 -> 寄存器分配器 -> 中间代码 -> 目标机器码生成器 -> 目标机器码

最后执行引擎会找到main()这个入口方法,并且执行其中的字节码指令。

最后,关于HelloWorld执行过程,基本上阐述完毕了,关于执行程序期间,JVM内存分配问题,是一个比较大的模块,我们下次再聊!!!

【思考解惑】加载阶段完成后,虚拟机会将Main.class的二进制字节流按照虚拟机所需的格式存储在方法区之中,然后在内存中实例化一个java.lang.Class类的对象,作为程序访问方法区中的这些类型数据的外部接口,实例化后的java.lang.Class类的对象也是存放在方法区中的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/566925.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matlab窗函数 响应,matlab窗函数设计方案.doc

matlab窗函数设计方案PAGE \* MERGEFORMATPAGE \* MERGEFORMAT 13目录TOC \o "1-3" \h \u HYPERLINK \l _Toc18947 目录 PAGEREF _Toc18947 1HYPERLINK \l _Toc5619 摘要 PAGEREF _Toc5619 2HYPERLINK \l _Toc16857 1 MATLAB的概况 PAGEREF _Toc16857 2HYPERLINK \l …

java接口文档生成工具_【分享】接口文档生成工具apipost

一、为什么要写接口文档?正规的团队合作或者是项目对接,接口文档是非常重要的,一般接口文档都是通过开发人员写的。一个工整的文档显得是非重要。项目开发过程中前后端工程师有一个统一的文件进行沟通交流开发,项目维护中或者项目…

matlab boll源码,【每日一策】Matlab量化交易策略之 布林带突破+头寸管理

function Strategyn(freq)%targetList traderGetTargetList();%获取目标资产信息HandleList traderGetHandleList();%获取账户句柄global record;global state;for k1:length(targetList);%--------------------仓位、K线、当前bar的提取-----------------------------%%获取…

jacobi matlab程序,jacobi迭代法实验MATLAB程序数值分析

jacobi迭代法实验MATLAB程序数值分析 例1. 求线性方程组 得近似解。精确解为x*[3,2,1]’。 解:对方程进行移项就得 记为Axb,或写为xB0 xf,其中 取初始值,代入原方程组可得再将把它代入可得.反复利用这个计算过程,得到一向量序列和…

docker部署python web应用_安装docker并部署web项目

一.docker简介1、docker定义:docker是一个用来装应用的容器,就像杯子可以装水,笔筒可以装笔,书包可以放书一样。你可以把“hello world!”放到docker中,也可以把网站放到docker中,你可以把任何你…

java 水印乱码,linux java程序加水印及中文乱码方案

在linux,centos环境下的,生成的带文字的水印图片在显示为方框乱码。img.setFont(new Font("宋体", Font.BOLD, 20));写中文进入图片之前必须设置字体,而且这个字体必须支持中文,否则就会出现乱码或者方框、问号等等。Bu…

resultmap拿不到数据_阿里巴巴国际站每日电商运营工作数据表格

一、日常运营工作表1.数据日报统计每天的流量数据:2.数据周报统计每周的流量数据:3.数据月报统计每月流量数据:前半部分后半部分4.P4P月数据总览统计P4P的流量数据:大图包含上面所有的方案5.P4P日数据总览6.爆款数据统计每月统计爆…

php oracle新增数据类型,oracle数据类型

数据类型字符串类型字符串类型用来存储字符串数据char用于描述定长的字符型数据0~2000nchar用来存储Unicode字符集的定长字符型数据0~1000varchar2用于描述可变长的字符型数据0~4000nvarchar2用来存储unicode字符集的可变长字符型数据0~1000long用来存储边长的字符串0~2GBvarch…

jenkins ssh 远程部署_Jenkins部署jar到远程服务器

首选得确定Jenkins的安装完整,还有插件的安装,除默认插件,此处必备插件:Publish Over SSH,SSH plugin,因为我源码在gitlab所以关于gitlab的插件我也安装了,看各自的情况选择安装。然后到系统管理…

jieba分词_Jieba.el – 在Emacs中使用jieba中文分词

jieba.el在Emacs中使用jieba中文分词众所周知, Emacs并没有内置中文分词系统, 以至于 forward-word 和 backward-word 以及 kill-word 等以单词为单位的操作只能粗暴的标点符号进行确定所谓”词汇”, (其实是中文里的句子).jieba.el 利用nodejieba对buffer中的中文句子进行分割…

python的request请求401_Python模拟HTTPS请求返回HTTP 401 unauthorized错误

Python模拟HTTPS请求返回HTTP 401 unauthorized错误开始是使用的 httplib模块,代码如下:header {"Content-type": "application/json", "Accept": "*/*" }params { ‘source‘:‘en‘, ‘target‘:‘es‘, ‘…

java php 女生数量,萌妹子告诉你php和java如何选

原标题:萌妹子告诉你php和java如何选phpPHP 独特的语法混合了C、Java、Perl 以及 PHP 自创新的语法。它可以比 CGI 或者 Perl 更快速的执行动态网页PHP具有非常强大的功能,所有的CGI的功能PHP都能实现,而且支持几乎所有流行的数据库以及操作系…

new 结构体指针_Go:我应该用指针替代结构体的副本吗?

logo对于许多 golang 开发者来说,考虑到性能,最佳实践是系统地使用指针而非结构体副本。我们将回顾两个用例,来理解使用指针而非结构体副本的影响。1. 数据分配密集型让我们举一个简单的例子,说明何时要为使用值而共享结构体&…

Oracle 同音字查询,汉字的演变过程100字,汉字的演变图片

中国的汉字博大精深,历史悠久。早期甲骨文主要是线条图,统一而优美。后来,它逐渐演变成一幅由笔画组成的图画,这是当代汉字的基础商周时期,甲骨文逐渐成为金文。西周时期,青铜器铸造,铭文用于记…

python音频聚类_python实现鸢尾花三种聚类算法(K-means,AGNES,DBScan)

python实现鸢尾花三种聚类算法(K-means,AGNES,DBScan)发布时间:2020-08-31 21:23:24

matlab 实验数据 传递函数,《传递函数MATLAB实验》.ppt

传递函数MATLAB实验 练习: 各种典型环节的阶跃响应曲线 1、比例环节(K) 从图形库浏览器中拖曳Step(阶跃输入)、Gain(增益模块)、Scope模块到仿真操作画面,连接成仿真框图。 结论:比例环节 K 改变,则放大倍数改变。 2、积分环节( 1…

localstorage存储大小_Cookie 已凉,Web 存储该这么做!

本文经授权转自公众号CSDN(ID:CSDNnews)作者 | 浪里行舟责编 | 郭芮随着移动网络的发展与演化,我们手机上现在除了有原生 App,还能跑“WebApp”——它即开即用,用完即走。一个优秀的 WebApp 甚至可以拥有和原生 App 媲美的功能和体…

三折线弹塑性滞回模型matlab,动力弹塑性滞回模型-迈达斯汇总.doc

9-1 概要非线性抗震分析方法可分为非线性静力分析方法和非线性动力分析方法。其中非线性静力分析方法(静力弹塑性分析)因其理论概念易于理解、计算效率高、整理结果较为容易等原因为设计人员所广泛使用。但是由于静力弹塑性分析存在反映结构动力特性方面的缺陷、使用的能力谱是…

python实现knn算法鸢尾花_Python学习之knn实现鸢尾花分类

# K近邻算法# 导入相关库文件import numpy as npimport matplotlib.pyplot as plt#import pandas as pdfrom sklearn import neighbors, datasets# 导入数据集,数据集sklearn自带,X与y一一对应dataset datasets.load_iris()# 获取鸢尾花前两列花萼长度和…

php获取变量数据类型,php如何确定变量的数据类型

在php中,数据类型有:Boolean 布尔类型、Integer 整型、Float 浮点型、String 字符串、Array 数组、Object 对象、Resource 资源类型、NULL;知道一个数据的类型,能够更加有效地进行代码逻辑处理。1、使用 var_dump() 函数,可以获取…