一,前言
在我们运行一个简单的“Hello world!”程序时,JVM中所有的类都会被提前加载吗?
答案是否定的,这样做的话,就会造成巨大的性能开销。那类是如何加载在能让我们在程序中流畅的使用并运行程序的呢,下面就让我们来了解一下Java中的类加载机制与双亲委派模型吧!
二,类的生命周期
(1)加载
- 1.根据该类的完全限定名加载类的二进制字节流。
- 2.将静态储存转为MetaSpace元空间区的运行时储存。
- 3.在内存中生成一个该类的class对象,作为元空间中该类各种数据的访问入口。
(2)验证
确保该类的字节流信息符合当前JVM的要求,不会危害JVM自身的安全。
(3)准备
类变量是被static修饰的成员变量,在MetaSpace中分配内存空间,并设置初始值。
成员变量不会直接在元空间中分配内存,而是会随着对象被实例化,一起在堆区中分配内存空间。
(4)解析
将常量池中的符号引用替换为直接引用。
(5)初始化
是虚拟机执行类构造器的<clinit>()方法,开始真正执行程序代码的时候。
三,类的加载时机
主动加载
- 1.当程序中包含new(实例化对象) , getstatic(获取静态变量),putstatic(给静态变量赋值),invokestatic(调用该类的静态方法)四条字节码指令时,主动加载该类。
- 2.使用Java.lang.Reflect包下的反射方法时,主动加载该类。
- 3.加载一个类时,如果父类未被加载,就先加载该类的父类。
- 4.在虚拟机启动时,先加载该程序中定义的main()主方法。
- 5.当一个接口中定义了JDK8中的新方法,default方法,若程序继承了该接口,则先加载该接口下的default方法。
被动加载
除主动加载外,所有引用类的方式都不会出发加载机制,称为被动加载。
举例:(1)通过子类去加载父类的静态字段时,不会导致子类被加载。
(2)通过数组定义来引用类,不会触发该类的加载机制。
四,类加载器分类
从JVM虚拟机的角度出发:
启动类加载器:底层由C++实现,是虚拟机的一部分。
其他类加载器:由Java实现,继承自Java.lang.classLoader
从Java开发人员的角度来看:
启动类加载器:该类加载器负责将存放在<jre_Home>\lib目录中的,或者-Xbootclasspath
参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar名字不符合的类库即使放在 lib
目录中也不会被加载)类库加载到虚拟机内存中。例如java.util.*
,java.io.**
,java.lang.*
类等常用基础库都是由启动类加载器加载。启动类加载器无法被 Java
程序直接引用。
扩展类加载器: 该类由ExtClassLoader实现,负责将系统变量所指定路径中所有的类库都加载到类库中,例如:swing系列,xml解析器,js引擎等,以javax开头的扩展类库都由扩展类加载器加载。
应用程序类加载器:该类由AppClassLoader实现。也被称为系统类加载器,用于加载用户路径上指定的类库(classPath)。如果应用程序中没有自定义过类加载器,一般情况下,这个就是系统默认的类加载器。
五,双亲委派模型
应用程序的加载依靠三种类加载器相互配合,共同加载的。除此之外,还可以自定义类加载器。
类加载器之间的关系,被称为双亲委派模型。除了启动类加载器之外,其他的加载器都必须要有自己的父类加载器,但这种关系不是依靠继承完成的,而是通过组合关系实现的。
1.双亲委派模型的工作原理
一个类加载器首先会把类加载的请求转发到父类加载器,当父类加载器无法完成加载时,才会尝试自己加载。
2.双亲委派模型的作用
(1)使Java类随着它们的类加载器一起具有一种优先级的层次关系从而使得基础类得到统一,避免冲突。
(2)避免了多份同样字节码的类加载,比如sout()输出语句,如果每运行一次都在加载输出语句,就会造成内存的严重浪费,使用双亲委派模型就可以很好的避免重复加载的问题。(自下向上加载类)
3.双亲委派模型的具体实现
主要是在Java中的java.lang.classLoader类实现。
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;
}
}