JVM类加载器

在写这篇文章之前特意读了下十多年前的一本书的某些章节 《深入java虚拟机》,收获还是挺大,至少知道了类加载器在安全方面起到了至关重要的作用,废话不多说,来看看类加载器是什么。

我们知道我们写的java程序最终都要编译成class文件,这是一种二进制的文件,被设计的非常紧凑,因为这有利于class文件在网络中的传输,奠定java语言在分布式领域的优势,另一个优势是跨平台,也就是所谓的一次编译到处运行。当jvm执行class文件的时候,首先要做的肯定是去加载它,类加载器主要做的事情就是去加载class文件。JVM中的类加载机制是双亲委派,不知道为啥叫双亲,好奇怪的名字,姑且也这样叫吧,那么什么叫双亲委派呢?再解释这个概念的时候,先来看下JVM中默认提供的三种类加载器。

启动类加载器,bootstrap这个加载器由C++提供,java程序无法获取,此加载器也是加载器的祖宗,它没有父亲。确切的讲它加载的是jre/lib下面的jdk核心类库,也可以由-Xbootclasspath启动参数指定,值得一提的是该加载器只加载特定名字的jar,比如rt.jar,非法的jar它不去加载。

扩展类加载器(Extension ClassLoader),扩展类加载器由java语言本身实现,负责加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,我们是可以直接使用该加载器的,它是bootstrap加载器的孩子。

应用程序类加载器(Application
ClassLoader),也叫系统类加载器,一开始学习java的时候总会提到classpath的概念,没错,这个加载器就是加载我们指定的classpath下的类。它是扩展类加载器的孩子。同样是由java程序编写,我们也可以扩展它。

介绍完默认的三种类加载器,双亲委派的模型就很容易理解了,在jdk的最初版本的时候,类加载还不是这个模型,在jdk1.2的时候才正式有了这个概念。也就是当我们的系统类加载器试图通过loadClass去加载一个类的时候,会先把加载的动作传递给父加载器(如果有),就这样一层层的传递,如果最终的根加载器没有加载到该类,则依次返回,由子类加载,如果所有的类加载器都加载不到,则报ClassNotFoundException错误。之所以采用这样一个模型是因为考虑到安全性,假设我们自己定义了一个java.lang.Integer类的实现,并指定类加载器去加载,试想如果jvm加载的Integer类不是jdk提供的,而是我们自己写的,这将是非常危险的。但是采用委派的方式,我们自己定义的Integer类将永远不会得到加载的机会。

看下双亲委派源码:

protected synchronized Class<?> loadClass(String name, boolean resolve)
	throws ClassNotFoundException
    {
	// First, check if the class has already been loaded
	//首先检查这个类有没有被加载,这里的name是类的权限定名
	Class c = findLoadedClass(name);
	if (c == null) {
		//如果还没被加载,并且有父加载器,则委派给父加载器加载
	    try {
		if (parent != null) {
		    c = parent.loadClass(name, false);
		} else {
			//如果没有父亲,则使用bootstrap加载,可以想象,该方法最终是个native方法
		    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.
	        c = findClass(name);
	    }
	}
	//执行解析,链接动作
	if (resolve) {
	    resolveClass(c);
	}
	return c;
    }

实际上我们自己扩展的类加载器可以抛弃这种双亲委派的加载模型,而且有些时候还必须要这么做,双亲委派的一个特点是由父加载器加载的类对子加载器都可见,然而反过来确不行,而父加载器一般加载的都是底层基础的api,所以大多数情况都没有问题,但是有些特殊的情况,需要底层的api去调用用户实现的类,比如JNDI或者JDBC,JNDI的代码由启动类加载器去加载,目的就是对资源进行集中的管理和查找,各个厂商的都有自己的实现位于classpath下,JNDI要去调用其中的api,关于这种情况有个统称叫Service
Provider Interface简称SPI,那么问题来了,由于父类加载器不能调用到子加载器加载的代码,而我们现在确实有这么一种诉求。为了解决这个问题,在java中有一种线程上下文加载器的概念,通过该方法可以在线程上下文设置一个加载器,Thread.currentThread().setContextClassLoader(),如果未设置会从父线程来继承classloader,如果都为设置默认为app类加载器,有了这个方法,JNDI就可以拿到这个classLoader去加载SPI代码,也就是将加载的动作由子类向上请求逆向,由父加载器委托给子类来加载,从而实现SPI代码的可见性。

时间: 2024-08-26 14:23:38

JVM类加载器的相关文章

深入JVM类加载器机制,值得你收藏

先来一道题,试试水平 public static void main(String[] args) { ClassLoader c1 = ClassloaderStudy.class.getClassLoader(); ClassLoader c1Parent = ClassloaderStudy.class.getClassLoader().getParent(); ClassLoader c1ParentParent = ClassloaderStudy.class.getClassLoad

JVM类加载器ClassLoader解读

ClassLoader类加载器负责将类加载进入JVM中. ClassLoader的作用 (1)加载class文件进入JVM (2)审查每个类应该由谁加载,采用双亲委托机制 (3)将class字节码重新解析成JVM要求的对象格式 ClassLoader结构分析 protected final Class<?> defineClass(byte[] b, int off, int len)throws ClassFormatError{ return defineClass(null, b, of

JVM 类加载器的工作原理

Java类加载器的作用就是在运行时加载类.Java类加载器基于三个机制:委托.可见性和单一性.委托机制是指将加载一个类的请求交给父类加载器,如果这个父类加载器不能够找到或者加载这个类,那么再加载它.可见性的原理是子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类.单一性原理是指仅加载一个类一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类.正确理解类加载器能够帮你解决NoClassDefFoundError和java.lang.ClassNotFo

JVM类加载器原理与自定义类加载器

类加载器原理 JVM将class文件字节码文件加载到内存中, 并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class 对象,作为方法区类数据的访问入口. 类缓存 标准的Java SE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间.不过,JVM垃圾收集器可以回收这些Class过象. 类加载器数状结构 引导类加载器(bootstrap class loader) 它用来加载Java的核心库(JAVA_HOME/

十四.jvm类加载器

什么是类的加载? 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口. 1.加载.class文件的方式 从本地系统中直接加载 通过网络下载.class文件 从zip,jar等归档文件中加载.class文件 从专有数据库中提

JVM类加载器及Java类的生命周期

预定义类加载器(三种): 启动(Bootstrap)类加载器: 是用本地代码实现的类装入器,它负责将<Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar).由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作.扩展扩展(Extension)类加载器: 是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的.它负责将< Java_R

(二十七)JVM类加载器机制与类加载过程

一.Java虚拟机启动.加载类过程分析 下面我将定义一个非常简单的java程序并运行它,来逐步分析java虚拟机启动的过程. package org.luanlouis.jvm.load; import sun.security.pkcs11.P11Util; /** * Created by louis on 2016/1/16. */ public class Main{ public static void main(String[] args) { System.out.println(

JVM 类加载器命名空间深度解析与实例分析

一.创建Sample 1.创建实例 public class MyPerson { private MyPerson myPerson; public void setMyPerson(Object obj){ this.myPerson = (MyPerson)obj; } } 2.创建测试类 public class MyTest20 { public static void main(String[] args) throws Exception { MyTest16 loader1 =

Jvm 类加载器(一)

1.JVM的体系机构             2.类加载器ClassLoader 负责加载class文件,class文件在文件开头有特定的文件标识(CA FE BE AN),并且ClassLoader只负责class文件的加载,至于它是否可运行,则由Execution Engine决定. 3.类加载器ClassLoader2 虚拟机自带的加载器 1.启动类加载器(Bootstrap)  C++ 2.扩展类加载器(Extension) Java 3.应用程序类加载器(AppClassLoader)