平时我们都使用 idea、eclipse 等软件来编写代码,在编写完之后直接点击运行就可以启动程序了,那么这个过程是怎么样的?
总体过程
我们编写的 java 文件在由编译器编译后会生成对应的 class 字节码文件, 然后再将 class 字节码文件转给 JVM 。 JVM 会处理解析 class 文件,将其内部设置的类、方法、常量等信息全部提取出来,然后找到 main 方法开始一步一步编译成机器码并执行,中间会根据需要调用前面提取的数据。
那为什么不让 JVM 直接编译 java 文件呢?这样效率不是更高么?
首先要知道 java 之所以强大,原因之一就是 JVM 的强大。
强大之一是 JVM 是 " 跨平台 " 的。无论在哪种操作系统上执行,都可以转成对应的机器语言,不需要担心适配问题。
第二点就是 JVM 是 " 跨语言 " 的,因为 JVM 只认 class 文件,所以其他语言只需要一个编译器编译成 class 文件就可以使用 JVM 来编译执行了。
组件分析
根据上面的说明可以知道 java 程序执行的核心是通过 JVM 来实现的,那么就需要知道 JVM 内部是如何执行的。
JVM 内部可以分为四大部分,运行时数据区域、类加载系统、执行引擎、本地接口和本地方法库。
类加载系统:主要就是指类加载器,用于把 class 数据文件加载到运行时数据区域,然后由数据区域来编译执行。
运行数据区域:搭配执行引擎来编译传来的文件中的代码,然后执行,并且根据需要通过本地方法接口调用本地方法。
执行引擎:主要用于代码的编译和 运行时对象的回收。
本地库接口和本地方法库:提供一些 java 无法实现,需要底层执行调用的方法,是 jvm 访问底层的重要途径。
类加载器
用于进行类的加载。
种类
一般分为启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器。图中的从自定义类加载器到启动类加载器一层一层使用箭头连接, 这种箭头并不是继承关系,而是上下级关系。上下级的联系是通过 ClassLoader 抽象类继承过来的 parent 属性设置的。
1、启动类加载器(Bootstrap ClassLoader)(引导类加载器),加载java 核心类库( /jre/lib/rt.jar), 无法被java程序直接引用,是用C++编写的 ,用来加载其他的类加载器(类加载器本质就是类),是所有加载器的父类。
2、拓展类加载器(Extension ClassLoader),用来加载java 的拓展库( <JAVA_HOME>/jre/lib/ext)。
3、系统类加载器(System ClassLoader )(应用程序类加载器),用来加载类路径下的 Java类
4、用户自定义类加载器,继承java.lang.ClassLoader类的方式实现。
官方文档中将类加载器分为引导类加载器和自定义类加载器,这是因为引导类加载器是使用其他语言实现的,而拓展类、系统类、自定义类加载器全部都是通过继承 ClassLoader 抽象类实现的,所以都统一被划分为自定义类加载器。
(完)