Java 运行原理详解
前言
阅读对象
本文是博主根据自己经验和查阅资料完成,如有不正确之处欢迎指正。本文主要针对Java语言有一定基础且想提高技术深度的人员。如果你还是一个小白推荐Java入门教程。如果你有以下几个疑问欢迎阅读
- Java文件(xxx.java)是如何转为字节码文件(xxx.class)?
- 字节码文件内存结构模型?
- Java虚拟机是如何加载字节码文件?
- Java虚拟机是如何执行代码?
目标
帮助读者更全面的理解Java语言从《Java规范》到《JVM规范》的运行原理,让你知其然更知其所以然。让性能优化不再是那么高不可攀。
java 运行流程
我相信所有Java开发人员对Java语言的main()方法并不陌生,它是启动一个Java虚拟机进程的入口方法。下面详细说明执行原理,为了方便读者理解使用如下代码示例来分析。
public class TestJava{
public static void main(String [] args ){
System.out.println("hello this is java");
}
}
执行流程
Java启动分析
main方法规范
main方法是由虚拟机直接调用,所以main方法受到很多的限制
- 必须是public修饰
- 返回值必须为void
- 必须是静态方法(static 修饰)
- 必须接收String类型的数组参数agrs
格式如下:
public static void main(String [] args ){
... }
编辑代码
通过代码编辑器(idea,eclipse等)根据《Java规范》编写代码,该代码文件称为Java源代码文件TestJava.java。
编译代码
将Java代码源文件TestJava.java通过JVM编译工具javac编译生成字节码文件TestJava.class。从开发者的角度由Java源文件到字节码文件仅仅使用Javac一个命令既完成,看似简单的动作实则完成了很复杂的转换。Javac的编译流程如图2
在Java语言中,Javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树,再遍历语法树生成线性的字节码指令流的过程。那么字节码到底是什么样子的呢?如果有这样的疑问请阅读java字节码内存模型
虚拟机类加载机制
Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、准备、解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。根据执行流程图可知类加载机制是在虚拟机内部执行,但是加载、校验、准备、解析和初始化是在程序运行期间完成的,这给Java极高的扩展性和灵活性,Java天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点实现的。例如,编写一个面向接口的应用程序,可以等到运行时再指定其实际的实现类,用户可以通过Java预置或自定义类加载器,让某个本地的应用程序在运行时从网络或其他地方加载一个二进制流作为其程序代码的一部分。这种动态组装应用的方式目前已广泛应用于Java项目中。
类加载器
Java虚拟机类加载是由内部完成,但是类加载器确可以在外部扩展。如图1所示类加载器不同的jdk版本实现有所区别,在1.2之前的类加载器没有严格规范加载模型,1.2~1.8使用了双亲委派模型,1.9开始添加了模块化功能所以将双亲委派模型做了扩展修改。
双亲委派模型
根据图1可知双亲委派模型是由启动类加载器(Bootstrap Class Loader)、扩展类加载器(Extension Class Loader)和应用程序类加载器(Application Class Loader)组成。通常开发人员可根据业务需求基于应用程序类加载器完成自定义类加载器。除了启动类加载器外其他加载必须存在上级类加载器,也可以通俗的理解为启动类加载器是顶级加载器。
启动类加载器
启动类加载器负责加载JAVA_HOME\lib目录或者被-Xbootclasspath参数所指定的路径中存放的,而且是Java虚拟机能够识别的(按照文件名识别,如rt.jar、tools.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机的内存中。该类加载器是由虚拟机内部实现由c++编写。
扩展类加载器
扩展类加载器负责加载JAVA_HOME\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库。根据“扩展类加载器”这个名称,就可以推断出这是一种Java系统类库的扩展机制,JDK的开发团队允许用户将具有通用性的类库放置在ext目录里以扩展Java SE的功能,在JDK 9之后,这种扩展机制被模块化带来的天然的扩展能力所取代。该类加载是由Java代码编写sun.misc.Launcher$ExtClassLoader,所以开发人员直接可以扩展该类来自定义类加载器。
应用程序类加载器
应用程序类加载器是ClassLoader类中的getSystem-ClassLoader()方法的返回值,所以有些场合中也称它为“系统类加载器”。它负责加载用户类路径 (ClassPath)上所有的类库,开发者同样可以直接在代码中使用这个类加载器。如果程序中没有使用自定义类加载器默认使用该加载器完成类加载。
自定义类加载器
自定义类加载器通常是基于应用程序类加载来完成。主要用来个性化加载类,比如网络加载类,特定路径加载类和破坏双亲委派模型等。
双亲委派模型类加载流程
为了更清楚的描述这里用TestJava.class字节文件来举例(其他类加载流程也是一样),TestJava.class是通过自定义类加载器完成加载,加载流程如下: