JVM---类加载子系统
作用
/**
* 【JVM-类加载子系统-作用】
* 负责 从文件系统 或 网络中加载class文件;
* ClassLoader 只负责 class文件的加载,至于 是否可以运行,由 Execution Engine决定;
* 加载的 类元数据信息 存放于 方法区的内存空间;
* (除了类元数据信息外,方法区还会存放运行时常量池信息:字符串字面量、数字常量[这部分常量信息 是class文件中常量池部分的内存映射])
*/
类加载
加载:
将类的二进制数据(通常是以.class文件形式存在的字节码)从磁盘或其他存储设备上读取到内存中、并创建代表该类的Class对象
链接:
主要目的是 确保被加载的类信息符合Java语言规范,并且为方法执行做必要的准备工作;
初始化:
执行类的初始化代码,包括静态变量赋值和静态块的执行;
/**
* 【JVM-类加载子系统-类加载】
* 加载loading:
* a,通过 类的全限定名 获取定义此类的 二进制字节流;
* 加载class文件方式:
* 本地文件系统;
* 网络;
* jar,war,zip包...
* 运行时计算生成:动态代理...
* 其他文件生成:JSP
* ...
*
* b,将 定义类的 静态存储结构 转化为 方法区的运行时数据结构;
* c,在 内存中 生成一个代表 该类的java.lang.Class对象,作为 方法区 这个类的各种元数据信息 的访问入口;
*
*
* 链接linking:
* a,验证verify:
* 文件格式校验
* 开头是否以0xCAFEBABE
* ...
* 元数据验证
* 进行语义分析:是否有父类?是否继承final类?...
* 字节码验证
* 符号引用验证
*
* b,准备prepare:
* 为 static变量 在方法区 分配内存,并 赋初始值;
* 对于 final static 变量,编译时就已经设置初始值,准备阶段 显式 初始化;
*
* c,解析resolve:
* 将 常量池 中的 符号引用 转化为 直接引用;
*
*
* 初始化initialization:
* 执行 <clinit>() 的过程;
*
* <clinit>():
* 1、来源:
* javac编译器 自动收集 类中 所有 static 变量的 赋值内容 (包括 static{} 的内容);
*
* 2、指令执行顺序 :
* 声明顺序 ;
* eg:
* static {
* a = 2;
* System.out.println(a); // 报错:非法前向引用
* }
*
* private static int a = 1;
*
* 先使用-后声明:
* static变量a 在linking-prepare时 已经赋初始值0,所以 初始化阶段执行<clinit>() 是可以的;
* 【仅允许赋值,不允许调用】;
*
* <clinit>():
* iconst_2
* putstatic
* iconst_1
* putstatic
* return
*
* 3、如果 类 中没有 static 变量 、没有 static 代码块, javac编译后不会生成 <clinit>();
* <init> :
* 类的构造器;
* 任何一个类声明后,内部至少存在一个类的构造器;
*
* 4、执行 子类的 <clinit>() 前 ,必须 保证 父类的<clinit>()执行完毕;
*
* 5、JVM 必须 保证 一个类的<clinit>() 在多线程 下 被 同步加锁;
*
*/
类加载器
/**
* 【JVM-类加载子系统-类加载器】
* JVM支持2种类型的类加载器:
* 引导类加载器Bootstrap ClassLoader:
* 自定义类加载器:
* JVM规范中,将所有派生于ClassLoader的类加载器 称为 自定义类加载器;
*
* sun.misc.Launcher 是一个JVM的入口应用;
* public class Launcher {
* static class AppClassLoader extends URLClassLoader {}
*
* static class ExtClassLoader extends URLClassLoader {}
* }
*
* java.net.URLClassLoader
* public class URLClassLoader extends SecureClassLoader implements Closeable {}
*
* java.security.SecureClassLoader
* public class SecureClassLoader extends ClassLoader {}
*
* java.lang.ClassLoader
* public abstract class ClassLoader {}
*
*
* 引导类加载器、拓展类加载器、系统类加载器、用户自定义类加载器 的关系:
* 包含关系 (不是上下层、不是继承关系)
*
* 类加载器之间的这种层级关系,称为 类加载器的双亲委派模型;
* 双亲委派模型 要求 除了顶层的启动类加载器外,其余的类加载器应有自己的父类加载器;
* 父子关系不会以继承的关系实现,而是使用组合关系来复用父类加载器;
*
* a,引导类加载器Bootstrap ClassLoader
* 使用 C/C++实现,嵌套在JVM内部;
* 不继承自java.lang.ClassLoader,无父类加载器;
* 加载 拓展类加载器 、系统类加载器;
* 出于安全考虑,Bootstrap ClassLoader只加载java、javax、sun等开头的类;
* 作用:
* 加载 Java的核心类库
* (java_home/jre/lib/rt.jar、resources.jar或sun.boot.class.path路径下的内容)
* 用于JVM自身需要的类;
*
* URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
* for(URL url : urLs){
* System.out.println(url.toExternalForm());
* }
*
* //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/resources.jar
* //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/rt.jar
* //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/sunrsasign.jar
* //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/jsse.jar
* //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/jce.jar
* //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/charsets.jar
* //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/jfr.jar
* //file:/Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/classes
*
* b,拓展类加载器ExtClassLoader
* 使用Java实现,sun.misc.Launcher.ExtClassLoader;
* 继承自ClassLoader;
* 作用:
* 加载 jre/lib/ext 目录下的类库;
*
* String property = System.getProperty("java.ext.dirs");
* for(String path : property.split(":")){
* System.out.println(path);
* }
*
* // /Users/an/Library/Java/Extensions
* // /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/jre/lib/ext
* // /Library/Java/Extensions
* // /Network/Library/Java/Extensions
* // /System/Library/Java/Extensions
* // /usr/lib/java
*
*
* c,系统类加载器
* 使用Java实现,sun.misc.Launcher.AppClassLoader;
* 继承自ClassLoader;
* 作用:
* 加载 classpath或java.class.path 下 用户 自定义类;
*
* d,用户自定义类加载器
* 为什么要自定义?
* a,隔离 加载类;
* b,修改 类的加载方式;
* c,拓展 加载源;
* d,防止 源码泄漏;
*
* 自定义步骤:
* 第一种:
* a,继承 java.lang.ClassLoader
* b,重写 findClass()
*
* 第二种:
* 如果没有过多的逻辑,可以直接 继承 java.net.URLClassLoader;
*
* java.lang.ClassLoader
* public abstract class ClassLoader {
*
* // 根据 class全限定名称 加载Class
* public Class<?> loadClass(String name) throws ClassNotFoundException {}
* }
*
*/
public class Launcher {
private ClassLoader loader;
private static String bootClassPath = System.getProperty("sun.boot.class.path"); // bootStrap类加载器 加载 sun.boot.class.path下的内容
public Launcher() {
ExtClassLoader var1;
try {
var1 = ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
this.loader = AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
}
static class ExtClassLoader extends URLClassLoader {
public static ExtClassLoader getExtClassLoader() throws IOException {
final File[] var0 = getExtDirs();
try {
return (ExtClassLoader) AccessController.doPrivileged(new PrivilegedExceptionAction<ExtClassLoader>() {
public ExtClassLoader run() throws IOException {
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
MetaIndex.registerDirectory(var0[var2]);
}
return new ExtClassLoader(var0);
}
});
} catch (PrivilegedActionException var2) {
throw (IOException)var2.getException();
}
}
private static File[] getExtDirs() {
String var0 = System.getProperty("java.ext.dirs"); // 扩展类加载器 加载 java.ext.dirs下的内容
File[] var1;
if (var0 != null) {
StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
int var3 = var2.countTokens();
var1 = new File[var3];
for(int var4 = 0; var4 < var3; ++var4) {
var1[var4] = new File(var2.nextToken());
}
} else {
var1 = new File[0];
}
return var1;
}
}
static class AppClassLoader extends URLClassLoader {
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
final String var1 = System.getProperty("java.class.path"); // 系统类加载器 加载 java.class.path下的内容
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<AppClassLoader>() {
public AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : sun.misc.Launcher.pathToURLs(var2);
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
}
}
AppClassLoader
sun.misc.Launcher.AppClassLoader

ExtClassLoader
sun.misc.Launcher.ExtClassLoader

Class加载流程
sun.misc.Launcher.AppClassLoader#loadClass

public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
int var3 = var1.lastIndexOf(46);
if (var3 != -1) {
SecurityManager var4 = System.getSecurityManager();
if (var4 != null) {
var4.checkPackageAccess(var1.substring(0, var3));
}
}
if (this.ucp.knownToNotExist(var1)) {
Class var5 = this.findLoadedClass(var1);
if (var5 != null) {
if (var2) {
this.resolveClass(var5);
}
return var5;
} else {
throw new ClassNotFoundException(var1);
}
} else {
return super.loadClass(var1, var2);
}
}
super.loadClass(var1, var2)

java.lang.ClassLoader#loadClass(java.lang.String, boolean)
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
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) {
// If still not found, then invoke findClass in order
// to find the class.
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;
}
}

java.lang.ClassLoader#loadClass(java.lang.String, boolean)

java.lang.ClassLoader#findBootstrapClassOrNull 使用bootstrap累加载器加载
java.lang.ClassLoader#findClass 使用自定义类加载器加载
双亲委派机制
/**
* 【JVM-类加载子系统-双亲委派机制】
* JVM 对 class文件 采用的是 按需加载(使用该类时,再进行加载);
* 加载 某个class文件时,采用 双亲委派机制 (把 加载请求 交由 上级加载器 加载);
* 如果 上级 类加载器 不能完成 类的加载,才由 本级类加载器 去加载;
*
* 优势:
* 避免类的重复加载;
* 保护程序安全,防止 核心 API 被篡改;
* eg:
* 自定义 java.lang.String类...
*/
其他
/**
* 【JVM-类加载子系统-其他】
* 在同一个JVM中,表示2个class对象是否一致的必要条件: 类全限定名 + ClassLoader
* a, 类的全限定名称相同
* b, 加载该类的ClassLoader相同
*
* 类的主动使用和被动使用
* 主动使用:
* 创建类的实例;
* 访问某个类或接口的static变量 或 对static变量赋值;
* 调用类的static方法
* 反射(Class.forName(""))
* 初始化一个类的子类
* JVM启动时 被标明为 启动类的类
* 动态语言支持:
*
* 被动使用:
* 除主动使用外,都是被动使用;
*
* 主动使用与被动使用的区别:
* 被动使用 不会导致 类的初始化;
*
*/
浙公网安备 33010602011771号