Java类加载器(  CLassLoader ) 死磕 1、2: 

导入 & 类加载器分类

本小节目录

1.导入

1.1. 从class文件的载入开始

1.2. 什么是类加载器

2. JAVA类加载器分类

2.1. 操作系统的环境变量

2.2. Bootstrap ClassLoader(启动类加载器)

2.3. Extention ClassLoader (扩展类加载器)

2.4 Appclass Loader(应用类加载器)

2.5. 加载器启动次序

2.6. 阅读源码,查看加载器启动顺序

2.7. 本节小结


1.导入

.1.从class文件的载入开始

我们都知道在Java中程序是运行在虚拟机中,我们平常用文本编辑器或者是IDE编写的程序都是.java格式的文件,这是最基础的源码,但这类文件是不能直接运行的。如我们编写一个简单的程序HelloWorld.java,Java虚拟机并不能直接识别我们平常编写的.java源文件,所以需要javac这个命令转换成.class文件,class文件是字节码格式文件。
关键性的一步来了,.class如果加入JVM中呢?
这就需要用到Java的类加载器。

1.2.什么是类加载器

ClassLoader翻译过来就是类加载器,普通的java开发者其实用到的不多,但对于某些框架开发者来说却非常常见。理解ClassLoader的加载机制,也有利于我们编写出更高效的代码。ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去,程序就可以正确运行了。但是,jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载。想想也是的,一次性加载那么多jar包那么多class,那内存不崩溃。本文的目的也是学习ClassLoader这种加载机制。

2.JAVA类加载器分类

讲解类加载器,首先回顾一下系统的环境变量。

2.1.操作系统的环境变量

操作系统中,与java相关的重要的系统环境变量,你知道有哪些吗?
初学java的时候,最害怕是配置环境变量了,关键是不理解,只能战战兢兢地照着书籍上或者是网络上的介绍进行操作。太不照顾新手了,很多菜鸟就是因为卡在环境变量的配置上,遭受了太多的挫败感。

java相关的环境变量的配置,主要有3个:
JAVA_HOME、PATH、CLASSPATH
具体的配置方式,与操作系统相关。 在Windows下编程时,可以通过系统属性的环境变量界面,配置以上的三个环境变量。
下面介绍一下三个环境变量的含义。

一:JAVA_HOME

指的是你JDK安装的位置,一般默认安装在C盘,如:

C:\Program Files\Java\jdk1.8.0_131

二:PATH

PATH变量,是系统之前就已经一直存在和一直使用的。是系统指令和命令的搜索路径。设置PATH变量的方法是:
PATH=%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;%PATH%;

在原来的PATH路径上添加JDK目录下的bin目录和jre目录的bin。将Java的bin 路径包含在PATH当中后,在命令行窗口就可以直接键入Java命令的名字了,而不再需要键入它的全路径。比如编译java源文件的javac命令,和执行字节码的java命令。

如果没有将Java的bin路径加入到path中,执行javac命令的方法如下:

C:\Program Files\Java\jdk1.8.0_91\bin\javac  test.java

设置了设置PATH变量之后,执行javac命令的方法如下:

javac  test.java



三:CLASSPATH

CLASSPATH是指向jar包路径和class类路径。
一个配置的例子如下:

CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar

需要注意的是前面的. 点号,代表可以在当前目录搜索java 类文件。

以上的三个Java 系统的环境变量,都与Java的类加载器密切相关。Java语言系统自带有三个类加载器,具体如下图:

Java类加载器(死磕 1-2)-LMLPHP

Java类加载器(死磕 1-2)-LMLPHP

2.2.Bootstrap ClassLoader(启动类加载器)

Bootstrap ClassLoader是最顶层加载器,主要加载核心类库,%java.home%\lib下的rt.jar、charsets.jar和class等。

注意,这里的 %java.home% 不是JAVA_HOME环境变量,但是与JAVA_HOME环境变量的值相关。有很多文章介绍到这里的时候,将 %java.home% 与JAVA_HOME搞混淆了。
专门澄清一下:

%java.home%  指的是JAVA语言运行时的系统属性java.home;JAVA_HOME环境变量,指的是JDK在操作系统中的路径。

通过下面的语句可以获得%java.home% 其值:

    private static void showjavahome()
{
String javahome = System.getProperty("java.home");
Logger.info("javahome = " + javahome);
}

Java类加载器(死磕 1-2)-LMLPHP

返回的结果是:

javahome = C:\Program Files\Java\jdk1.8.0_131\jre

对比一下,而前一个小节,我们设置的JAVA_HOME环境变量的值为:Java类加载器(死磕 1-2)-LMLPHP

所以,两者是不一样的。Bootstrap ClassLoader(启动类加载器)加载的类列表清单,保存在系统属性%sun.boot.class.path% 中,通过获取系统属性的值,可以查看。
代码如下:

private static void showbootpath()
{
String path = System.getProperty("sun.boot.class.path");
Logger.info("sun.boot.class.path = " + path);
}

Java类加载器(死磕 1-2)-LMLPHP

结果如下:

sun.boot.class.path =  C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\sunrsasign.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\classes

Java类加载器(死磕 1-2)-LMLPHP

通过结果可以看出,系统属性%sun.boot.class.path%中的jar包和类路径,都处于系统属性%java.home% 下。

另外需要注意的是:可以通过启动jvm时指定一个参数选项,来改变Bootstrap ClassLoader的加载目录。这个参数选项为 -Xbootclasspath。
例如:

java -Xbootclasspath:c:/path1/jar1.jar;c:/path2/jar2.jar  Test

被指定的文件追加到默认的bootstrap路径中。

Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,比如int.class,String.class都是由它加载。

 

2.3.Extention ClassLoader (扩展类加载器)

Extention ClassLoader (扩展的类加载器),加载系统属性%java.ext.dirs%目录下的jar包和class文件。

Extention ClassLoader (扩展的类加载器)加载的类列表清单,保存在系统属性%java.ext.dirs% 中,通过获取该系统属性的值,可以查看。
代码如下:

  private static void showextpath()
{
String path = System.getProperty("java.ext.dirs");
Logger.info("java.ext.dirs = " + path);
}

返回的结果如下:

java.ext.dirs =
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext;
C:\WINDOWS\Sun\Java\lib\ext

另外需要注意的是:还可以加载-D java.ext.dirs选项指定的目录。

2.4. Appclass Loader(应用类加载器)

与前面两个不同,Appclass Loader 是应用程序的类加载器。Bootstrap ClassLoader 和 Extention ClassLoader, 主要是用于虚拟机的系统类加载。

Appclass Loader(应用类加载器)的作用,加载当前应用类路径classpath的所有类。

应用类加载器,也称为SystemAppClass (系统应用类加载器)。

Appclass Loader(应用类加载器)加载的类列表清单,保存在系统属性%java.class.path % 中,通过获取该系统属性的值,可以查看。
代码如下:

 private static void showclasspath()     {
String path = System.getProperty("java.class.path");
Logger.info("java.class.path = " + path);
}

返回的结果如下:

java.class.path =  C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;
C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;
D:\疯狂创客圈 死磕Java\code\out;
D:\疯狂创客圈 死磕Java\code\lib\cglib-3.2.3.jar;
D:\疯狂创客圈 死磕Java\code\lib\asm-5.0.4.jar;
C:\Program Files\JetBrains\IntelliJ IDEA 2017.1.1\lib\idea_rt.jar

回顾一下前面设置的操作系统下的环境变量CLASSPATH,如下:

CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar

对比之后,发现有点不对。上面输出的系统属性%java.class.path%的值,与配置的CLASSPATH值不一致,原因是什么呢?
因为我们这里使用了开发工具IntelliJ IDEA,在工程中重新配置了依赖包,相当于将系统属性%java.class.path%的值,重新配置了一下。
具体如下图:

Java类加载器(死磕 1-2)-LMLPHP

Java类加载器(死磕 1-2)-LMLPHP

2.5.加载器启动次序

前面介绍了的3个类加载器,通过启动JVM启动Java程序的时候,怎样知道具体哪个先行启动呢?

这里,可以先揭晓答案:

1.  Bootstrap CLassLoader

2.  Extention ClassLoader

3.  AppClassLoader

再啰嗦一句:BootstrapClassLoader、ExtClassLoader、AppClassLoader通过查阅相应的系统属性sun.boot.class.path、java.ext.dirs和java.class.path来加载资源文件的。

Java类加载器(死磕 1-2)-LMLPHP

2.6.阅读源码,查看加载器启动顺序

三大加载器的启动顺序,可以通过阅读源码来查看。

JVM在启动是,会执行sun.misc.Launcher 类。在该类中,创建一个Extention ClassLoader 扩展类加载器,和一个应用类加载器AppClassLoader。
源码如下:

public class Launcher {
private static Launcher launcher = new Launcher();
private static String bootClassPath =
System.getProperty("sun.boot.class.path"); private ClassLoader loader; public Launcher() {
// 创建Extention ClassLoader 扩展类加载器
ClassLoader extcl;
try {
extcl = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
throw new InternalError(
"Could not create extension class loader", e);
} // 创建应用类加载器AppClassLoader
try {
loader = Launcher.AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
throw new InternalError(
"Could not create application class loader", e);
} .....
}
}

Java类加载器(死磕 1-2)-LMLPHP

在上面的代码中,看到了扩展类加载器和应用类加载器的启动,没有看到Bootstrap Loader的启动。
为什么呢?
从启动次序来说,Bootstrap Loader作为jvm核心的核心,加载位于%java.home% 清单中的JVM 的绝对核心和基础的类,毫无疑问,绝对是首先启动的。由于它是基础加载器,大多数版本jvm的bootstrap loader都不是java实现,或者不是纯java的实现。理论上,Bootstrap ClassLoader 不应该叫他是一个java类。因为,它已经完全不用java实现了。Bootstrap ClassLoader是在jvm启动时,就被构造起来的,负责java平台核心库。

所以,在sun.misc.Launcher 类中,看不到Bootstrap Loader的启动。

完整的三大加载器的启动顺序如下:
1、开始执行java命令后,首先寻找jre目录,寻找jvm.dll,并初始化JVM;
2、产生一个Bootstrap Loader(启动类加载器);
3、创建Extended Loader(标准扩展类加载器);
4、创建AppClass Loader(系统类加载器),并将其父加载器(parent loader)设为Extended Loader。

这里讲到了父加载器,这是一种加载器之间的重要的关系——加载器之间的层次关系。

JVM初始化sun.misc.Launcher并创建Extension ClassLoader和AppClassLoader实例。并将ExtClassLoader设置为AppClassLoader的父加载器。

Bootstrap没有父加载器,而且它是祖宗,它是作为所有父加载器为空的加载器的父加载器。

呵呵,捋一捋,这句话是不是很绕。

后文中,将对这种层次关系,展开进行详细解读。



2.7.本节小结

上面简单介绍了3个ClassLoader。说明了它们加载的路径。

并且还提到了-Xbootclasspath和-D java.ext.dirs这两个虚拟机参数选项。 

上面也介绍了三大加载器之间的启动顺序。为后面介绍加载器之间的层次关系,打下了一个铺垫。

本节的代码的类为:SysPropertiesShow.java,

所在的路径为:com.crazymakercircle.classLoaderDemo.base

源码:

代码工程:  classLoaderDemo.zip

下载地址:在疯狂创客圈QQ群文件共享。

疯狂创客圈:如果说Java是一个武林,这里的聚集一群武痴, 交流编程体验心得
QQ群链接:疯狂创客圈QQ群

无编程不创客,无案例不学习。 一定记得去跑一跑案例哦

类加载器 死磕 系列

1.导入

2. JAVA类加载器分类

3. 揭秘ClassLoader抽象基类

4. 神秘的双亲委托机制

5. 入门案例:自定义一个文件系统的自定义classLoader

6. 基础案例:自定义一个网络类加载器

7. 中级案例:设计一个加密的自定义网络加载器

8. 高级案例1:使用ASM技术,结合类加载器,解密AOP原理

9. 高级案例2:上下文加载器原理和案例



05-08 08:08