一聚教程网:一个值得你收藏的教程网站

热门教程

JVM加载class文件代码原理机制实例

时间:2022-06-29 01:48:08 编辑:袖梨 来源:一聚教程网

本篇文章小编给大家分享一下JVM加载class文件代码原理机制实例,文章代码介绍的很详细,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看。

一、JVM简介

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

Java语言的一个非常重要的特点就是与平台的 无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改的运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的及其指令执行。这就是Java能够“一次编译,到处运行”的原因。

二、JVM的组成部分

由图可以看出,JVM是运行在操作系统之上的,它与硬件没有直接的交互。

1. 类加载器 Class Loader

类加载器的作用是加载类文件到内存,比如编写一个HelloWorld.java程序,然后通过javac编译生成class文件。由Class Loader将class文件加载到内存中。但是Class Loader加载class文件有格式要求。

注意:Class Loader只管加载,只要符合文件结构就加载,至于能不能运行,是由Execution Engine负责。

2. 执行引擎 Exexution Engine

执行引擎也叫作解释器,负责解释命令,提交操作系统执行。

3. 本地接口 Native Interface

本地接口的作用是为了融合不同的编程语言为Java所用。它的初衷是为了融合C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须要有一个聪明的、睿智的调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载加载native libraries。目前该方法只有在与硬件有关的应用中才会使用,在企业级应用中已经比较少见,因为现在的异构领域间的通信很发达,比如可以使用Socket通信,也可以使用WebService等。

4. 运行数据区 Runtime data area

运行数据区使整个JVM的重点。我们所写的程序都被加载到这里,之后才开始运行,Java生态系统如此的繁荣,得益于该区域的优良自治。

三、JVM加载class文件的原理机制

1. Java中的所有类,必须被装载到JVM中才能运行,这个装载工作是由JVM中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中,作用就是在运行时加载类。

Java类加载器基于三个机制:委托、可见性和单一性。

(1)委托机制是指加载一个类的请求交给父类加载器,如果这个父类加载器不能够找到或加载这个类,那么再加载它。

(2)可见性的原理是子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。

(3)单一性原理是指一个类仅被加载一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类。

2. Java中的类大致分为三种:

(1)系统类

(2)扩展类

(3)由程序员自定义的类

3. 类装载有两种方式

(1)隐式装载:

程序在运行过程中当碰到通过new等方式生成类或者子类对象、使用类或者子类的静态域时,隐式调用类加载器加载对应的的类到JVM中。

(2)显式装载:

通过调用Class.forName()或者ClassLoader.loadClass(className)等方法,显式加载需要的类。

4. 类加载的动态性体现

一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载再运行,他总是把保证程序运行的基础类一次性加载到JVM中,其他类等到JVM用到的时候再加载,这样是为了节省内存的开销,因为Java最早就是为嵌入式系统而设计的,内存宝贵,而用到时再加载这也是Java动态性的一种体现。

5. Java类加载器

Java中的类加载器实质上也是也是类,功能是把类加载入JVM中,值得注意的是JVM的类加载器有三个,原因有:一方面是为了分工明确,各自负责各自的区块,另一方面为了实现委托模型。

层次结构如下:

BootStrap Loader(引导类加载器) ----- 负责加载系统类

ExtClassLoader(扩展类加载器) ----- 负责加载扩展类

AppClassLoade(应用类加载器)r ----- 负责加载应用类

6. 类加载器之间如何协调工作的

Java中有三个类加载器,碰到一个类需要加载时,Java采用委托模型机制来协调和区分该由哪个类加载器完成。简单来说就是,“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入”,如果Parent找不到,那么才由自己依照自己的搜索路径搜索类。

实例一:

package ClassLoaderTest;
public class ClassLoaderTest {
    public static void main(String[] args) {
        ClassLoader c1 = ClassLoaderTest.class.getClassLoader();
        System.out.println(c1);
        ClassLoader c1Parent = c1.getParent();
        System.out.println(c1Parent);
        ClassLoader c1Root = c1Parent.getParent();
        System.out.println(c1Root);
    }
}

执行结果:

可以看出ClassLoaderTest是由AppClassLoader加载器加载的。AppClassLoader的Parent加载器是ExtClassLoader。但是ExtClassLoader的Parent是null,在Java中是无法获取的。

实例二:

public class Test2 {
    public void test(){
        System.out.println(Test2.class);
        System.out.println(this.getClass());
        System.out.println(Test2.class.getClassLoader());
    }
}
public class Test1 {
    public static void main(String[] args) {
        System.out.println(Test1.class.getClassLoader());
        Test2 test2 = new Test2();
        test2.test();
    }
}

执行结果:

7. 预先加载和依需求加载

Java运行环境为了优化系统,提高程序的执行速度,在JRE运行的开始会将Java运行所需要的基本类采用预先加载(pre-loading)的方法全部加载到内存当中,因为这些单元在Java程序运行的过程当中要经常使用的,主要包括JRE的rt.jar文件里面所有的.class文件。

当java.exe虚拟机开始运行以后,它会找到安装在机器上的JRE环境,然后把控制权交给JRE,JRE的类加载器会自动将lib目录下的rt.jar基础类别文件库加载进内存,这些文件是Java程序执行所必需的,所以系统在开始就将这些文件加载,避免以后的多次IO操作,从而提高程序执行效率。然而我们在程序中需要使用自定义的类的时候就要使用依需求加载(load-on-demand)的方式,就是在Java程序需要用到的时候再加载,以减少内存的消耗。

8. ClassLoader中一些 重要的方法

9.什么地方适用类加载器

最经典的例子就是AppletClassLoader,他被用来加载Applet使用的类,而Applet大部分是在网上使用,而非本地的操作系统使用。使用不同的类加载器,你可以从不同的源地址加载同一个类,它们被视为不同的类。J2EE使用多个类加载器加载不同地方的类,例如War文件由Web-app类加载器加载,而EJB-JAR中的类由另外的类加载器加载。有些服务器也支持热部署,这是由类加载器实现。你也可以使用类加载器来加载数据库或者其他持久层的数据。

10. 类加载器的阶层体系

Java类加载器的工作原理:

当执行Java的.class文件的时候,java.exe会帮助我们找到jRE,接着找到JRE内部的jcm.dll,这才是真正的Java虚拟机器,最后加载动态库,激活Java虚拟机器。虚拟机激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成后,就会产生第一个类加载器-----Bootstrap Loader,Bootstrap Loader是由C++撰写而成,这个Bootstrap所做的初始工作中,除了一些基本的初始化动作之外,最重要的就是加载Launcher.java之中的ExtClassLoader,并设定其parent为null,代表其父加载器为BootstrapLoader。然后Bootstrap loader再要求加载Launcher.java之中的AppClassLoader,并设定其parent为之前产生的ExtClassLoader实体。这两个类加载器都是以静态类的形式存在的。注意:Launcher E x t C l a s s L o a d e r . c l a s s 与 L a u n c h e r ExtClassLoader.class与Launcher ExtClassLoader.class与LauncherAppClassLoader.class都是由Bootstrap Loader所加载,所以Parent和由哪个类加载器加载没有关系。

三者之间的关系:

Bootstrap Loader <—(extends)-----ExtClassLoader <—(extends)—AppClassLoader

这三个类加载器构成了Java的类加载体系。它们分别从以下的路径寻找程序所需要的类:

Bootstrap Loader:sun.boot.class.path

ExtClassLoader:java.ext.dirs

AppClassLoader:java.class.path

这三个参数可以通过System.getProperty()函数得到具体对应的路径。

热门栏目