【JAVA笔记——术】Java ClassLoader类加载机制详解

时间:2022-04-08 19:36:06

Java ClassLoader

三种类加载器

WIki : Java_Classloader

JAVA类加载器实现了一部分 JRE加载JAVA CLASSES到 JVM 的功能。

ClssLoader 实现了懒加载,并且使得JVM不必关心加载文件以及所使用的文件系统。

类加载器虽然只用于实现类的加载动作,但它在JAVA程序中所起到的作用远远不限于类加载阶段。对于任意一个类,都需要有由加载它的类加载器和这个类本身一同确立其在JAVA虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。这意味着唯一确定一个JAVA的类需要确认
JavaClassloader + Java Class 相同。

JVM启动时,这三种加载器将被启动:

1 Bootstrap class loader

C++语言实现,是虚拟机自身一部分,其他都是JAVA实现继承java.lang.ClassLoader。负责加载

双亲委派模型

双亲委派模型Parents Delegation Model

双亲委派模型是通过Composition模式实现
【JAVA笔记——术】Java ClassLoader类加载机制详解

双亲委派模型的基本思路是,一个类加载器收到了类加载请求,自己不会加载,而是调用父类加载器去完成

sun.misc.Launcher中继承关系如下

【JAVA笔记——术】Java ClassLoader类加载机制详解

ClassLoader解释如下

java.lang.ClassLoader
A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the binary name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class. A typical strategy is to transform the name into a file name and then read a “class file” of that name from a file system.

ClassLoader class uses a delegation model to search for classes and resources. Each instance of ClassLoader has an associated parent class loader. When requested to find a class or resource, a ClassLoader instance will delegate the search for the class or resource to its parent class loader before attempting to find the class or resource itself. The virtual machine’s built-in class loader, called the “bootstrap class loader”, does not itself have a parent but may serve as the parent of a ClassLoader instance.
Class loaders that support concurrent loading of classes are known as parallel capable class loaders and are required to register themselves at their class initialization time by invoking the registerAsParallelCapable() method. Note that the ClassLoader class is registered as parallel capable by default. However, its subclasses still need to register themselves if they are parallel capable.
In environments in which the delegation model is not strictly hierarchical, class loaders need to be parallel capable, otherwise class loading can lead to deadlocks because the loader lock is held for the duration of the class loading process (see loadClass methods).
Normally, the Java virtual machine loads classes from the local file system in a platform-dependent manner. For example, on UNIX systems, the virtual machine loads classes from the directory defined by the CLASSPATH environment variable.
However, some classes may not originate from a file; they may originate from other sources, such as the network, or they could be constructed by an application. The method defineClass converts an array of bytes into an instance of class Class. Instances of this newly defined class can be created using Class.newInstance.

基本可以总结如下:

  1. 类加载器可以直接定位或者生成类信息
  2. 类加载器是通过双亲委派模型加载Delegation Model
  3. 默认的三种加载器都已经实现并行加载,但用户自定义加载器若需要并行加载,需要自行配置,通过调用registerAsParallelCapable()

ClassLoader.loadClass 源码如下

protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 从父加载器中查询类加载结果,避免重复加载
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//若父加载器不为空 调用父节加载器进行加载
//若无父加载器 调用启动类加载器加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}

if (c == null) {
// 若果父加载器依然无法加载 在调用本身findClass方法进行类加载
long t1 = System.nanoTime();
c = findClass(name);

// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}

Class 与 ClassLoader

一个类实例化是被其他的类或方法进行调用,其实际的状况如下:

ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();

类加载器在进行类加载之后,再对类进行实例化。

这时候有个疑问,我们经常获取的类的Class究竟是什么?根据源码定义

Instances of the class Class represent classes and interfaces in a running Java application. An enum is a kind of class and an annotation is a kind of interface. Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.

我们可以知道:

  1. Class其实是类在Java虚拟机的实例引用
  2. 枚举类型实际是一种Class
  3. 注解实际是一种接口
  4. 对象数组实际上也是一种Class,在GetClass之后返回的是 元素类 + 数组长度
  5. 基本类型也是一种Class,但基本类型数组不会有返回结果

通常我们获取ClassLoader是通过获取

public ClassLoader getClassLoader() {
ClassLoader cl = getClassLoader0();
if (cl == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
}
return cl;
}

getClassLoader0()是Jvm本地方法,实际上是获取当前类加载时的加载器。Class在运行初会加载本地方法集,这部分会在Jvm中实现。Class的构造方法是私有的,只能有JVM在实例化Class的时候进行调用,因此getClassLoader0()实际就是从JVM中获取加载器相关信息。

private static native void registerNatives();
static {
registerNatives();
}

/*
* Private constructor. Only the Java Virtual Machine creates Class objects.
* This constructor is not used and prevents the default constructor being
* generated.
*/
private Class(ClassLoader loader) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
}