JVM加载class文件的原理机制是什么?

2021-04-02 17:17发布

8条回答
快乐的一只小青蛙
2楼 · 2021-04-06 13:36

1、装载。根据查找路径找到相对应的class文件,然后导入。

2、链接。链接又可以分为3个小的步骤,具体如下。

    1)检查。检查待加载的class文件的正确性。

    2)准备。给类中的静态变量分配存储空间。

    3)解析。将符号引用转换成直接引用(这一步是可选的)。

3、初始化。对静态变量和静态代码块执行初始化工作。

小小人
3楼 · 2021-04-07 21:07

JVM中类的加载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。

由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。

类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。

当类被加载后就进入连接阶段,这一阶段包括

验证:为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

准备:为静态变量分配内存并设置默认的初始值。

解析:将符号引用替换为直接引用。

最后JVM对类进行初始化,包括:1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语句。

类的加载是由类加载器完成的,类加载器包括:启动类加载器(BootStrap)、扩展类加载器(Extension)、应用程序类加载器(Application)。      

从Java 2(JDK 1.2)开始,类加载过程采取了双亲委派模型(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是启动类加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。


-绝世帅逼
4楼 · 2021-04-10 21:46

ava语言是一种具有动态性的解释型语言,类(class)只有被加载到JVM中后才能运行。当运行指定程序时,JVM会将编译生成的.class文件按照需求和一定的规则加载到内存中,并组织成为一个完整的Java应用程序。这个加载过程是由类加载器来完成的,具体来说,就是由ClassLoader和它的子类来实现的。类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中。

 

类的加载方式分为隐式加载与显式加载两种。隐式加载指的是程序在使用new等方法创建对象时,会隐式地调用类的加载器把对应的类加载到JVM中。显式加载指的是通过直接调用class.forName()方法来把所需要的类加载到JVM中。

 

任何一个工程项目都是由许多个类组成的,当程序启动时,只把需要加载的类加载到JVM中,其他类只有被使用到的时候才会被加载,采用这种方法,一方面可以加快加载速度,另外一方面可以节约程序运行过程中对内存的开销。此外,在Java语言中,每个类或接口都对应一个.class文件,这些文件可以被看成一个个可以被动态加载的单元,因此当只有部分类被修改时,只需要重新编译变化的类即可,而不需要重新编译所有文件,因此加快了编译速度。

 

在Java语言中,类的加载是动态的,它并不会一次性将所有的类全部加载后再运行,而是保证程序运行的基础类(例如基类)完全加载到JVM中,至于其他类,则在需要时才加载。在Java语言中,可以把类分为3类:系统类、扩展类和自定义类。Java针对这3种不同的类提供了3中类型的加载器,这3种加载器的关系如下:

以上这三个类是如何协调工作来完成类的加载呢?

其实,它们是通过委托的方式实现的。具体而言,就是当有类需要被加载时,类加载器会请求父类来完成这个载入工作,父类会使用其自己的搜索路径来搜索需要被载入的类,如果搜索不到,才会由子类按照其搜索路径来搜索待加载的类。下例可以充分说明类加载器的工作原理:

 

 
  1. package com.js;

  2. /**

  3. * 说明类加载器的工作原理

  4. * @author jiangshuai

  5. *

  6. */

  7. public class TestLoader {

  8. public static void main(String[] args){

  9. //调用Class加载器

  10. ClassLoader clApp = TestLoader.class.getClassLoader();

  11. System.out.println(clApp);

  12. //调用上一层Class加载器

  13. ClassLoader clExt = clApp.getParent();

  14. System.out.println(clExt);

  15. //调用根部Class加载器

  16. ClassLoader clBoot = clExt.getParent();

  17. System.out.println(clBoot);

  18. }

  19. }

运行结果:

sun.misc.Launcher$AppClassLoader@2a139a55
sun.misc.Launcher$ExtClassLoader@7852e922
null
 

从上例可以看出,TestLoader类是由AppClassLoader来加载的。另外需要说明的一点是,由于Bootstrap Loader是用C++语言来实现的,因此,在Java语言中是看不到它的,所以此时程序会输出null。

 

类加载的主要步骤分为以下3步:

1、装载。根据查找路径找到相对应的class文件,然后导入。

2、链接。链接又可以分为3个小的步骤,具体如下。

1)检查。检查待加载的class文件的正确性。

2)准备。给类中的静态变量分配存储空间。

3)解析。将符号引用转换成直接引用(这一步是可选的)。

3、初始化。对静态变量和静态代码块执行初始化工作。


我的网名不再改
5楼 · 2021-04-11 16:16

ava语言是一种具有动态性的解释型语言,类(class)只有被加载到JVM中后才能运行。当运行指定程序时,JVM会将编译生成的.class文件按照需求和一定的规则加载到内存中,并组织成为一个完整的Java应用程序。这个加载过程是由类加载器来完成的,具体来说,就是由ClassLoader和它的子类来实现的。类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中。

 

类的加载方式分为隐式加载与显式加载两种。隐式加载指的是程序在使用new等方法创建对象时,会隐式地调用类的加载器把对应的类加载到JVM中。显式加载指的是通过直接调用class.forName()方法来把所需要的类加载到JVM中。

 

任何一个工程项目都是由许多个类组成的,当程序启动时,只把需要加载的类加载到JVM中,其他类只有被使用到的时候才会被加载,采用这种方法,一方面可以加快加载速度,另外一方面可以节约程序运行过程中对内存的开销。此外,在Java语言中,每个类或接口都对应一个.class文件,这些文件可以被看成一个个可以被动态加载的单元,因此当只有部分类被修改时,只需要重新编译变化的类即可,而不需要重新编译所有文件,因此加快了编译速度。

 

在Java语言中,类的加载是动态的,它并不会一次性将所有的类全部加载后再运行,而是保证程序运行的基础类(例如基类)完全加载到JVM中,至于其他类,则在需要时才加载。在Java语言中,可以把类分为3类:系统类、扩展类和自定义类。Java针对这3种不同的类提供了3中类型的加载器,这3种加载器的关系如下:

以上这三个类是如何协调工作来完成类的加载呢?

其实,它们是通过委托的方式实现的。具体而言,就是当有类需要被加载时,类加载器会请求父类来完成这个载入工作,父类会使用其自己的搜索路径来搜索需要被载入的类,如果搜索不到,才会由子类按照其搜索路径来搜索待加载的类。下例可以充分说明类加载器的工作原理:

 

 
  1. package com.js;

  2. /**

  3. * 说明类加载器的工作原理

  4. * @author jiangshuai

  5. *

  6. */

  7. public class TestLoader {

  8. public static void main(String[] args){

  9. //调用Class加载器

  10. ClassLoader clApp = TestLoader.class.getClassLoader();

  11. System.out.println(clApp);

  12. //调用上一层Class加载器

  13. ClassLoader clExt = clApp.getParent();

  14. System.out.println(clExt);

  15. //调用根部Class加载器

  16. ClassLoader clBoot = clExt.getParent();

  17. System.out.println(clBoot);

  18. }

  19. }

运行结果:

sun.misc.Launcher$AppClassLoader@2a139a55
sun.misc.Launcher$ExtClassLoader@7852e922
null
 

从上例可以看出,TestLoader类是由AppClassLoader来加载的。另外需要说明的一点是,由于Bootstrap Loader是用C++语言来实现的,因此,在Java语言中是看不到它的,所以此时程序会输出null。

 

类加载的主要步骤分为以下3步:

1、装载。根据查找路径找到相对应的class文件,然后导入。

2、链接。链接又可以分为3个小的步骤,具体如下。

1)检查。检查待加载的class文件的正确性。

2)准备。给类中的静态变量分配存储空间。

3)解析。将符号引用转换成直接引用(这一步是可选的)。

3、初始化。对静态变量和静态代码块执行初始化工作。


我是大脸猫
6楼 · 2021-04-11 22:05

一、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);

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

执行结果:


可以看出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();

    }

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

执行结果:


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实体。这两个类加载器都是以静态类的形式存在的。注意:LauncherE 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与LauncherExtClassLoader.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()函数得到具体对应的路径。


是年糕麻麻啊
7楼 · 2021-04-12 09:19

1、装载。根据查找路径找到相对应的class文件,然后导入。

2、链接。链接又可以分为3个小的步骤,具体如下。

    1)检查。检查待加载的class文件的正确性。

    2)准备。给类中的静态变量分配存储空间。

    3)解析。将符号引用转换成直接引用(这一步是可选的)。

3、初始化。对静态变量和静态代码块执行初始化工作。


当运行指定程序时,JVM会将编译生成的.class文件按照需求和一定的规则加载到内存中,并组织成为一个完整的Java应用程序。这个加载过程是由类加载器来完成的,具体来说,就是由ClassLoader和它的子类来实现的。类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中。

lucky璐呀
9楼 · 2022-03-10 09:19

在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备、解析。

1、装载:查找和导入类或接口二进制数据;

2、链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
3、校验:检查导入类或接口的二进制数据的正确性;
4、准备:给类的静态变量分配并初始化存储空间;
5、解析:将符号引用转成直接引用;
6、初始化:激活类的静态变量,初始化Java代码和静态Java代码块。


相关问题推荐

  • 回答 156

    对于每一位才开始接触JAVA的新手来说,先不要管算法和数据结构,大多数简单的程序不需要用到算法和数据结构,所以当你真正需要时再去学习。编程一段时间以后,你就会知道在哪些地方用到他们。这时知道算法的名字并了解它们的功能,然后动手去实践。当我们在去...

  • 回答 93

    2个都很好就业,更关键的是要学得到东西

  • 回答 12
    已采纳

    获取Map集合中所有的key可以通过map集合的keySet()方法获取例如:    Map map = new HashMap();    map.put(xx,xx); //存放数据    //.... 省略    Set set = map.keySet();    //可以通过迭代器进行测试    Iterator iter = set.iter...

  • 回答 56
    已采纳

    不同年龄,不同掌握程度,学历,找工作城市,面试能力这是一个多方面影响的结果,如果是平均值的话,全国平均薪资14k左右

  • 回答 38

    具体学多久,根据自己的学习力,自律性、解决问题能力来决定若系统性学习,跟着讲师的节奏走,大概半年左右,有专业的讲师把课程进行规划,尽心系统学习,有问题,讲师会帮忙解决,学习的效率很高,避免了自学中出现各种问题解决不了,而耽误很多时间,可能会...

  • BIO与NIO、AIO的区别2020-05-19 15:59
    回答 4
    已采纳

    IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。一、BIO     在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要...

  • 回答 23
    已采纳

    (1)idea启动时会有两个快捷方式,安装完后默认生成在桌面的是32位的idea的快捷方式,如果我们使用这个快捷方式运行大项目,一般都会很卡。解决方法是找到idea的安装目录,然后进入bin文件夹,找到名称为idea64的应用程序,右键他生成桌面快捷方式。以后每次...

  • Java方法的命名规则2021-04-06 19:07
    回答 31

    ava是一种区分字母的大小写的语言,所以我们在定义变量名的时候应该注意区分大小写的使用和一些规范,接下来我们简单的来讲讲Java语言中包、类、变量等的命名规范。(一)Package(包)的命名Package的名字应该都是由一个小写单词组成,例如com、xuetang9、compan...

  • 回答 2

    public class Point {    private int x;    private int y;    public int getX() {        return x;    }    public void setX(int x) {        this.x = x;    }    public int getY() {        return y;    } ...

  • 回答 6

    经典版单例模式public class Singleton {        private static Singleton uniqueInstance;//利用一个静态常量来记录singleton类的唯一实例。     private Singleton() {     }     public static  Singleton getInstance()...

  • 回答 3

    哈希表的长度一般是定长的,在存储数据之前我们应该知道我们存储的数据规模是多大,应该尽可能地避免频繁地让哈希表扩容。但是如果设计的太大,那么就会浪费空间,因为我们跟不用不到那么大的空间来存储我们当前的数据规模;如果设计的太小,那么就会很容易发...

  • 回答 14

    1. DOM(Document Object Model)        DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准。DOM是以层次结构组织的节点或信息片断的集合。这个层次结构允许开发人员在树中寻找特定信息。分析该结构通常需要加载整个文档和构造层次结构,然后才...

  • 回答 19

    1)作用不同: throw用于程序员自行产生并抛出异常; throws用于声明在该方法内抛出了异常2) 使用的位置不同: throw位于方法体内部,可以作为单独语句使用; throws必须跟在方法参数列表的后面,不能单独使用。3)内容不同: throw抛出一个异常对象,且只能是...

  • 回答 11

    基本执行过程如下:1)程序首先执行可能发生异常的try语句块。2)如果try语句没有出现异常则执行完后跳至finally语句块执行;3)如果try语句出现异常,则中断执行并根据发生的异常类型跳至相应的catch语句块执行处理。4)catch语句块可以有多个,分别捕获不同类型...

  • 回答 20

    100-199 用于指定客户端应相应的某些动作。 200-299 用于表示请求成功。 300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。 400-499 用于指出客户端的错误。 400 语义有误,当前请求无法被服务器理解。 401 当前请求需要用户验证...

  • 回答 16

    异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java编译...

没有解决我的问题,去提问