让我们展示我的操作环境。我在Mac上操作。首先找到Mac的Java地址。来自?/.bash_profile,您可以看到
Java的家直接
如果现在有Java类Com.lxl.jvm.math类,则其中有一个主要方法
此方法非常简单。通常,我们可以直接执行主方法。它可以运行程序。那么如何加载和运行整个过程呢?为什么结果可以得到结果?
让我们看一下答案问题的类加载过程(宏观过程),如下所示:
评论:
1. Windows上的Java启动程序是Java.exe,在Mac下
2.我们了解,C语言部分是Java部门是需要掌握的一部分。
步骤1:Java调用基础JVM.DLL文件创建Java虚拟机(此步骤由C ++实现)。这里的java.exe是C ++编写的代码,Call jvm.dll也是C ++底层的函数。通过调用jvm.dll文件(DLL文件等效于Java的JAR软件包),该文件将创建Java虚拟机。Java虚拟机的启动由C ++程序实现。
步骤2:在启动虚拟机的过程中,将创建一个引导加载程序的实例。该引导的加载程序由C语言实现。然后,JVM虚拟机开始。
步骤3:接下来,C ++语言将调用Java的启动程序。我刚刚创建了Java虚拟机,Java虚拟机中有许多启动程序。其中一个程序称为启动器。该类的全名是Sun.misc.launcher.通过启动此Java类,该类将被加载并创建许多其他类加载程序。这些加载程序是真正启动并加载磁盘的字节代码文件。
步骤4:真正加载本地磁盘的字节代码文件,然后开始执行主方法。(此步骤将详细介绍,如何加载本地磁盘的字节代码文件。)
步骤5:执行主方法后,指南加载程序将启动C ++调用以销毁JVM
以上是启动主要方法,该类中加载的所有过程
下面,让我们看一下如何将我们的com.lxl.math加载到Java虚拟机中?
上面的com.lxl.jvm.math类最终将生成类字节码文件。字节代码文件如何加载到JVM虚拟机上?
有五个步骤加载:加载,验证,准备,分析,初始化。那么这五个步骤是什么?让我们来看看
我们的班级在哪里?在磁盘(例如:目标文件夹下的类文件)中,我们必须首先将类类加载到内存。加载到存储区域后,不简单地将其转换为二进制字节代码文件,他将经历一系列过程。例如:验证,准备,分析,初始化等,将这些列的信息转换为内部元素信息,并将其放在内存中。让我们看一下特定过程
步骤1:将类课程加载到Java虚拟机的内存。在加载到内存之前,将有一系列操作。第一步是验证字节代码。
步骤2:例如,验证字节代码是否正确加载,例如:打开一个字节代码文件。看着它,感觉就像是乱码,实际上不是。实际上,其中每个字符串都具有相应的含义。那么我们可以替换文件中的内容吗?当然,它不能成功。.因此,第一步:验证,要验证什么?
验证字节代码是否正确:格式是否正确。内容是否符合Java虚拟机的规格。
步骤3:准备完成后,下一步是准备。准备工作是什么?例如,我们的数学数,他将首先分配数学中的静态变量
在制备过程中,将给出这两个变量的初始值。这个初始值不是真正的值。例如,initdata的初始值为0。如果它是布尔类型,则将其分配给false。换句话说,FU的制备stagethe值是由JVM固定的,而不是我们定义的值。如果最终的常数,例如公共静态最终int名称=“ Zhangsan”,那么当他初始化它时,它直接是“ Zhangsan”。这是静态变量的初始值
步骤4:分析分析过程。分析过程略有复杂。解析是将“符号参考”转换为直接参考。
什么是符号参考?
例如,我们程序中的主要方法。写作是固定的,我们可以将主作为符号。例如,上述initdata,int,static,我们可以称其为符号,Java虚拟机中的Java虚拟机内有一个专业术语,称他为符号。这些符号将对应于内存中的地址。将“符号引用”转换为直接引用,这意味着诸如主,initdata,int等的符号将其转换为相应的内存地址。此地址ISIT是代码的直接引用。根据直接参考的值,我们可以知道代码在哪里。然后让代码真正运行。
将符号引用转换为“内存地址”。这种专业术语称为静态链接。上面的分析过程等同于静态链接的过程。在加载期间,将符号转换为内存地址。有一个静态链接。然后有一个与之相对应的动态链接。
什么是动态链接?
例如:以上代码,仅当我运行到Math.compute()时,然后返回加载Compute()方法。换句话说,将加载方法解析为内存地址。只有当它运行到这条线时,它将被解析。
让我们看一下装配代码
使用此指令查看数学的二进制文件。实际上,此文件是上面的二进制代码文件。
看看那里是什么?
班级的名称,大小,修改时间,大版本,小版本,访问装饰表格等。
还有一个恒定的池常数池。这个恒定的池中有很多事情。我们专注于中间的哪一行。第一列表示一个恒定徽标。该标识符可以在其他地方使用。
这些标识将在以后使用,例如主要方法
这是#2#3#4,它是标识符的引用。
第一句话:new有一个数学()。让我们看看如何编写大会?
新 +#2。#2是什么?转到常量池,#2代表数学课
您想在这里说的是Math.com pupute()。加载类时,此方法不会加载到内存。
加载类后,我们可以理解“分析”作为静态加载过程。通常,像静态方法(例如主方法)一样,获得其他常数静态方法将直接加载到内存中,因为考虑到性能,它们加载了它们。它不会改变,它将直接转换为进入内存中的代码位置。
像Math.compute()方法一样,在加载过程中可能会更改的方法(例如计算是多态性,有多个实现),然后当加载初始化时,我们不会称呼他会打电话给谁,只能运行然后,您可以知道代码的实现,因此,运行时,可以在操作过程中检查他在内存中的位置。这个过程是动态的加载
步骤5:将类的静态变量初始化为指定值。执行静态代码块。例如,代码
在准备阶段,它将其分配到0,在初始化阶段,它将分配给集合666
将类加载到方法区域后,它主要包括运行常数池,类型信息,字段信息,方法信息以及加载程序的引用,与类实例的引用相对应。
这是什么意思?也就是说,当一个类加载到内存时,此类的常数具有常数名称,类型,域信息等。方法名称,返回值类型,参数类型,方法分数和其他符号信息将受到影响。负载并放入不同的区域。
注意:如果在操作过程中其他类中的主类使用,则将逐渐加载这些类,这意味着懒惰的加载。使用时,加载。
这里定义了两个A和B类。当使用哪个时,该类将被加载。例如,在主要方法中,不使用B,因此不会将他加载到内存中。
操作结果
我们看到A类已加载,并且B类未加载,因为B类仅声明并且没有使用。
摘要如下:
该类主要通过类加载器加载。Java中有几种类型的装载机
1.在Bootstrap classloader(Bootstrap classLoader)的加载过程中,该指南类加载程序在启动虚拟机的过程中的目的是什么?
引导装载机主要负责加载最大的Java类型。这些库位于JRE目录**的LIB目录中。例如:rt.jar,charset.jar,等。
2.扩展加载程序(Ext Class Loader)扩展加载程序主要用于加载扩展JAR软件包。加载JAR的目录位于JRE目录的LIB/EXT扩展中的JAR包中
4.自定义类加载程序负责在用户的自定义路径下加载类软件包
指南加载程序由C ++实施以帮助我们,然后C ++语言将通过启动器类构建扩展加载程序(ExtClassLoader)和应用程序类加载程序(AppClassLoader),并建立它们之间的关系。
案例1:测试JDK的类加载程序
让我们看一下这个简单的代码,运行结果:
我们看到ExtClassLoader和AppClassLoader是启动器类的一部分。什么是发射器课程?
如上所述,启动器类是当激活启动器类时由C ++激活的类。此类指南加载程序加载并创建其他类加载程序。
因此,第一个Bootstrap Guidance Class Loader,那么为什么指导加载程序返回了NULL?
由于Bootstrap Guidance类加载器,他不是Java的对象,因此他是C ++的对象,因此在这里是看不见的。
案例2:BootstrapClassload和ExtClassLoader,AppClassloader
如上图所示,左侧是C语言程序代码实现,右侧是Java代码实现。称为C语言是发射器类。
从这个图中,我们可以看到C ++调用Java创建JVM启动器,其中之一是启动器。他实际上称Sun.misc.launcher类的GetLauncher()方法。然后,让我们从此方法开始,从此方法查看以查看如何运行?
我们看到lanucher.java类位于核心RT.Jar软件包中,Lanucher是一个非常核心的类。
我们看到GetLauncher()类直接返回启动器。启动器是静态对象变量,这是单身模式
C ++调用getLauncher() - >直接返回Lanucher对象,并且在构造类时已经初始化了启动器对象。那么,初始化时做了什么操作?接下来,看看他的结构方法。
在建设性方法中,首先定义ExtClassLoader。这是一个扩展装载机。扩展加载程序调用getextClassLoader()。接下来,看看GetExtClassLoader做了什么?
这是典型的多线程同步写作。
在这里,判断当前对象是否已将其初始化。如果不是,然后创建一个ExtClassLoader()对象以查看createexclassloader()方法发生的事情?
DROPILEDED是权限验证操作。我们可以首先在没有任何注意的情况下查看最后一句话。返回新启动器。extClassLoader(var1)。直接newClassLoader,其中参数为var1,该参数代表Ext扩展目录中的文件。
在ExtClassloader(文件[] var1)的方法中,这里的第一步是调用父级超级构造函数。ExtClassLoader继承了谁?我们可以看到他继承了UrlClassloader。
urlClassloader的用途是什么?实际上,联想可能可以猜测这个数字。这里有一些文件路径,通过文件路径加载类。
我们继续看单身的超级(父母)。我们继续下降,我们将看到调用ClassLoader接口的构造函数:
ExtClassLoader的父母在哪里设置?请注意,我们发现ExtClassloader的父类是null。
这是传递的父级加载程序,那么为什么这里的父级加载程序?因为extClassloader的母体加载程序是谁?无法直接调用它,因此将其设置为null。
实际上,ExtClassLoader在初始化阶段调用ExtClassLoader方法,并初始化ExtClassLoader类
接下来,我们回到了Launcher的建设性方法,以查看接下来的发射器?
可以看出,调整了AppClassLoader(VAR1)的GetAppClassLoader(VAR1)。这个方法。您需要注意参数var1。var1?
这是AppClassloader,一个应用程序类加载程序。该课程是一类负载我们自己的课程的类加载程序。他还继承了UrlClassloader。
让我们看一下getAppClassLoader(最终classloader var0)方法。该方法的参数是上面传递的ExtClassLoader。
这里的第一个句子是获取当前项目的类文件路径,然后将其转换为URL。并呼叫启动器。appClassLoader(var1x,var0).extClassLoader,下一步,我们输入此方法以查看:
AppClassloader直接调用其父类的结构。参数是类-Class路径集合和ExtClassloader
最后,我们看到ExtClassLoader传递给父变量。这是ClassLoader中定义的属性,ClassLoader类是所有类加载程序的父类。因此,我们还可以看到AppClassloader的父加载程序是ExtClassLoader
同时,我们还看到,当C ++启动JVM时,启动器启动类被调用。该启动类还加载了ExtClassLoader和AppClassLoader。
通过此演示,我们还可以看到AppClassloader的父类是ExtClassLoader,ExtClassLoader的父级课程
输出结果:
通过上面的源代码分析,我们发现指南加载程序创建并加载了扩展加载程序和应用程序类加载程序。扩展加载程序的母体加载程序是指南加载程序。该结构确定后一类的负载方法,即双边任命机制。