1、Java代码程序是如何运行起来的?
- jar包:将.class为后缀的Java程序代码打包为jar包,在服务器后台通过java -jar的命令运行程序;
- war包:将程序代码打包为war包,将其部署在例如Tomcat 的服务器上进行运行。
(1)我们的Java程序是以.java为后缀的文件,会通过编译器将代码编译为.class后缀的字节码文件,以供JVM识别和运行;
(2)Java的平台无关性,实现的基础就是虚拟机JVM和字节码存储格式。不同的操作系统可能对应不同版本的JVM,这些不同版本的JVM都可以将.class为后缀的字节码文件进行载入和运行,实现平台无关性。
- 类加载:JVM要运行这些字节码文件中的代码,就要将文件中包含
- 类加载器:通过类加载器,可以将编译好的.class字节码文件给加载到JVM中。
在类被加载到JVM后,需要通过链接过程将它合并到JVM的运行状态之中。链接包括三个步骤:
-
验证
验证加载进来的字节码是否符合JVM的规范,即验证加载的class文件是否被修改过等,验证通过才会运行。
-
准备
准备过程中,会为各种类分配内存空间,给内部(static修饰的)的类、变量分配内存空间并设置默认初始值。准备过程并不会运行代码。
-
解析
解析过程要确保这些被引用的类能被正确的找到,这个过程可能会导致其它的Java类被加载。
初始化
- 初始化过程主要操作是执行静态代码块和初始化静态变量,按照源代码中从上到下的顺序依次执行。
- 一个类被初始化之前,它的直接父类也需要被初始化
- 一个接口的初始化,不会引起其父接口的初始化。
总结:完成上面一些操作就可以在JVM执行运行Java程序。
2、什么是JVM类加载器机制和双亲委派机制模型?
Java中的类加载器大致分为两类,一类是系统提供的,另外一类则是由Java引用开发人员编写的。
系统提供的类加载器主要有:
- 启动类加载器(bootstrap class loader)
(1)主要负责加载我们机器上安装的Java目录下的核心类
(2)在Java安装目录下有一个lib目录,,这里有Java最核心的类库,支持Java系统的运行
(3)所以一旦JVM启动,首先会依托启动类加载器,去Java安装目录下的lib目录中启动系统核心类库。
- 扩展类加载器(extensions class loader)
Java安装目录下有一个lib/ext的目录,通过扩展类加载器来加载该目录下的文件,支持Java系统的运行,该目录下的类虽然没有启动类加载器加载的类那么核心重要,其重要性体现在辅助Java系统更好地运行
- 系统类加载器(AppClassLoader)
(1)主要负责加载ClassPath环境变量所指定的路径中的类(在开发工具中编写的类路径下的类)
(2)大致可理解为去加载写好的Java代码,把写好的类加载到内存里
自定义类加载器
开发人员可以通过继承java.lang.ClassLoader类的方式实现自己类加载器,以满足一些特殊需求。
类加载器的树状组织结构
除了启动类加载器之外,所有类加载器都有一个父类加载器。实现这种父子类关系,是为了满足后续的双亲委派机制。
双亲委派机制
在收到了类加载的请求之后,类加载器首先会将这个请求委托为父类加载器去完成,如果父类加载器无法完成加载,
才会由自己尝试去加载。
【举例】为什么使用双亲委派机制?
当JVM现在有需要加载一个User类的请求:
(1)应用程序累加器首先会问自己的父类扩展类加载器,你是否可以加载这个User类?
(2)此时扩展类加载器又会直接问自己的父类启动类加载器,你是否可以加载这个User类?
(3)当最顶层的父类启动类加载器发现在自己需要加载的Java目录下没找到这个User类,就下推加载权利给扩展类加载器这个子类;
(4)此时扩展类加载器如果也没有找到这个类,就下推加载权利给子类也就是刚开始收到类加载请求的应用程序类加载器进行加载;
(5)应用程序类加载器发现在自己加载的范围内,也就是写好的系统打包的jar包中,发现了这个User类,然后就将这个User类进行加载。
双亲委派机制的优点:
-
使得Java类随着它的类加载器,具备一种带有优先级的层级关系,避免加载的混乱
-
判断类是否相同,不仅要看类全名是否相同,还要看加载这个类的类加载器是否相同
-
例如类java.lang.Object,它存放在rt.jar之中,无论哪个类加载器要加载这个类,最终都是委托给处于模型最顶端
的启动类加载器进行加载,因此Object类在程序的各种加载器环境中都是同一个类。



