首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

从HelloWorld看JVM

这里,在聊Java运行方式前,我们需要先简单的了解下JVM,JRE,JDK。

JDK(Java Development Kit)是Java开发工具包,是Sun Microsystems针对Java开发员的产品。

JRE(Java Runtime Environment),是Java程序的运行的必备环境。也必须要有它,Java开发者开发的程序才能让用户使用。

JVM(Java Virtual Machine),也就是我们常说的Java虚拟机。我们的Java程序经过编译后会形成.class文件,也就是可执行文件,我们的可执行文件并不是我们操作系统的可执行文件,而是我们Java虚拟机的可执行文件。Java虚拟机在运行我们的可执行文件之后会再次编译成机器能够认识的语言并去执行。这就是我们Java虚拟机的存在意义,所以说我们的Java程序可以说是到处运行。下面这张图是官方提供的三者之间的关系,可以参看一下:

从上面的介绍中可以看到,我们的JVM并没有和硬件做直接的交互,而是通过我们的操作系统去进行交互的,如下图所示:

Class Loader

今天这里主要同大家介绍一下JVM中的类装载器。

从上面的图中我们可以看到,我们的可执行文件是经过 类加载器 加载后在进入到我们的数据区间的,也就是说,这个Class Loader 相当于我们的JVM的的入口,JVM 需要先将我们的 class 文件装载进去之后,才会去做后面的工作。那我们现在来看一下,这个Class Loader 是怎么去运作的。

ClassLoader 怎么加载

这里加载,主要有启动类加载器(Bootstrap),扩展类加载器(Extension),应用程序加载器(Application)。

虚拟机自带加载器。

启动类加载器:负责从启动类路径(%JAVA_HOME%\jre\lib\rt.jar)中加载类(C++),也就是我们的 rt.java.这个加载器会被赋予最高优先级;

扩展类加载器:负责加载 ext 目录内的类(%JAVA_HOME%\jre\lib);

应用程序类加载器: 负责加载应用程序级别类的路径。

用户自定义加载器: java.lang.ClassLoader 的子类,用户自己定义加载过程.

这里的类的加载有一个被称为双亲委托加载机制。

当使用类加载器加载一个类的时候,该类加载器会先判断该类是否被加载了,如果已经加载了就直接返回;如果没有加载那么将在加载请求委托给父类加载器,同样父类加载器也会先判断该类是否被加载了,如果已经加载了就返回,如果没有那么父类加载器继续委托它的父类加载器去加载,这样最终该加载请求到达了BootstrapClassLoader,如果BootstrapClassLoader成功加载,那么就返回加载的class对象,如果它也没有找到,那么就按照刚刚委托向上的这个路线,向下返回给下一个类加载器去加载,依次最后返回到发送委托请求的类加载器那里,如果还是没有加载成功,那么就由它自己来加载,如果它也没有找到,就抛出ClassNotFoundException异常。

加载都做了什么?

在这个加载的过程中,基本会完成三件事:

通过使用类的全限定义来获取到类的二进制字节流;

将这个类字节流里面的静态存储结构转为方法区;

在堆内存中会生成一个 java.lang.Class 的对象,也就是我们的入口。

我们直接看下 源码程序:

Verification这是我们的类加载后的一个校验,字节码校验器会去校验字节码是否正确,里面会有:文件格式验证(.class 文件规范,是否能被当前的JVM虚拟机处理等)、元数据验证(主要进行语义验证,符不符合Java语义)、符号引用验证(基于方法区的存储结构验证)、字节码验证(基于方法区的存储结构验证,确保运行不会出现危害虚拟机的行为)

下面的这张图可以看出我们的.class 加载后的一个基本的过程,这只是一个简单的模型图。

Hello World

我们这里还是使用入门程序:Hello World 来跟踪我们的程序。先看我的目录结构:

我这里也自己创建了一个 java.lang.String 的一个类,那程序会不会去加载这个类呢?我们跑一下就知道了。

我们进入到这个String里面,也会发现它用的String是他自己的,不是我的。这里面就是我前面说到的双亲委托加载机制,以及在校验时存在一个危险性的校验,也被称为沙箱机制。程序不允许我们这么干。

从上面的结果图里面我们也可以看到,我们的 HelloWorld 是 Application 加载器加载的,为什么是它呢?根据我们的双亲委托机制,一定是我们的 Extension 无法加载才能是 Application 加载器来加载的,也就是说我们的这个加载的顺序一定是:Bootstrap -> Extension -> Application .我们来看下他的父类加载器是什么:

可以看到,父类加载器是不是 Extension ? 结果给了我们答案。那要是有人问,为什么不输出 Bootstrap 加载器出来?这个能输出吗?可以思考一下的。

我们来做一件事:

首先,我在D 盘下创建一个 Test.java文件:

编译出来后,会生成一个 .class 文件,我们把 这个.class 文件剪切走,放在 %JAVA_HOME%\jre\classes 下,这个 classes 是自己创建的。

现在再讲Test.java 改了:

同样,编译后放在 ext/classes 目录下。最后再改:

编译后,我们开始运行。可以我们的设想,应该是:Hello Bootstrap -> Hello Extension -> Hello Application . 但是这三个一定只会出现一个。我们一个一个来验证。

运行后:

我们删除 在运行:

我们删除 ext 下的 Test.class 在运行:

好,结果出,真相大白。下一期跟大家介绍JVM后面的内容。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200316A0PYA500?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券