JVM系列四:类加载

Posted huanglf714

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM系列四:类加载相关的知识,希望对你有一定的参考价值。

类的生命周期

加载-》验证-》准备-》解析-》初始化-》使用-》卸载

技术图片

 

类加载过程

类加载包括以上的前五个过程:加载,验证,准备,解析,初始化

加载

1、主要完成三个工作

  • 通过类的完全限定名称来定位定义该类的二进制字节流
  • 将二进制字节流中的静态数据结构转化成方法区中的动态数据结构
  • 在堆中生成一个代表该类的Class对象,指向方法区的数据结构,作为访问方法区的入口

2、获取二进制字节流文件的途径

  • 从 ZIP 包读取,成为 JAR、EAR、WAR 格式的基础。
  • 从网络中获取,最典型的应用是 Applet。
  • 运行时计算生成,例如动态代理技术,在 java.lang.reflect.Proxy 使用 ProxyGenerator.generateProxyClass 的代理类的二进制字节流。
  • 由其他文件生成,例如由 JSP 文件生成对应的 Class 类

 

验证

验证类是合理可用的

1、文件格式验证:验证Class文件格式是否符合规范和是否能够被当前版本虚拟机解释

2、元数据验证:对类的元数据进行语义验证,确保元数据没有不符合java语义规范

3、字节码验证:对类的方法体进行验证,

4、符号引用验证:验证符号引用是否能够正确解析

 

准备

在方法区中为类变量分配内存,并赋予系统默认初始值,指0,null这种而不是代码中手工赋予的初始值

 

解析

将符号应用替换成直接引用

 

初始化

技术图片

 

类的初始化时机

主动引用

1、调用new、getststic,putstatic,invokestatic(读取或设置静态变量,调用静态方法)

2、通过反射实例化

3、初始化子类时,若父类还没有被初始化,则要先初始化父类

4、虚拟机启动时,加载包含Main的主类

 

被动引用

1、子类引用父类的静态字段,不会初始化子类

2、初始化数组类,不会初始化该类

3、常量在编译阶段就会读到常量缓冲池中,读取类的常量不会引起类的初始化

 

有且只有以上主动引用的四种情况才会引起类的初始化

 

类加载器

类加载器的定义与作用

类加载器的作用就是根据类的全限定名称获取相对应的Class二进制字节流文件,并将其加载到虚拟机内部

意义:任何一个类的唯一性是由类本身和加载该类的类加载器一起确定的

(两个类相等,包括类的 Class 对象的 equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回结果为 true,也包括使用 instanceof 关键字做对象所属关系判定结果为 true。)

 

分类

从虚拟机内部看

  • 启动类加载器(Bootstrap ClassLoader):用C++语言编写,是虚拟机的一部分
  • 其他类加载器:使用java实现,独立于虚拟机,继承于抽象类java.lang.ClassLoader

从开发人员角度看

  • 启动类加载器(BootStrap ClassLoader)
  • 扩展类加载器(Extension ClassLoader)
  • 应用程序类加载器(Application ClassLoader)

 

双亲委托模型

 

类加载器层次

技术图片

 

工作过程

 一个类加载器接到了加载类的请求,并不会立即加载,而是传递给它的上层类加载器,若上次类加载器无法加载,才会尝试自己加载

 

好处

使得 Java 类随着它的类加载器一起具有一种带有优先级的层次关系,从而使得基础类得到统一。

例如 java.lang.Object 存放在 rt.jar 中,如果编写另外一个 java.lang.Object 并放到 ClassPath 中,程序可以编译通过。

由于双亲委派模型的存在,所以在 rt.jar 中的 Object 比在 ClassPath 中的 Object 优先级更高,

这是因为 rt.jar 中的 Object 使用的是启动类加载器,而 ClassPath 中的 Object 使用的是应用程序类加载器。

rt.jar 中的 Object 优先级更高,那么程序中所有的 Object 都是这个 Object。

 

实现过程

loadClass() 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 ClassNotFoundException,此时尝试自己去加载。

 1 public abstract class ClassLoader 
 2     // The parent class loader for delegation
 3     private final ClassLoader parent;
 4 
 5     public Class<?> loadClass(String name) throws ClassNotFoundException 
 6         return loadClass(name, false);
 7     
 8 
 9     protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException 
10         synchronized (getClassLoadingLock(name)) 
11             // First, check if the class has already been loaded
12             Class<?> c = findLoadedClass(name);
13             if (c == null) 
14                 try 
15                     if (parent != null) 
16                         c = parent.loadClass(name, false);
17                      else 
18                         c = findBootstrapClassOrNull(name);
19                     
20                  catch (ClassNotFoundException e) 
21                     // ClassNotFoundException thrown if class not found
22                     // from the non-null parent class loader
23                 
24 
25                 if (c == null) 
26                     // If still not found, then invoke findClass in order
27                     // to find the class.
28                     c = findClass(name);
29                 
30             
31             if (resolve) 
32                 resolveClass(c);
33             
34             return c;
35         
36     
37 
38     protected Class<?> findClass(String name) throws ClassNotFoundException 
39         throw new ClassNotFoundException(name);
40     
41 

 

自定义类加载器

1、继承java.lang.ClassLoader

2、不用重写loadClass方法,但是需要重写findClass方法

 1 public class FileSystemClassLoader extends ClassLoader 
 2 
 3     private String rootDir;
 4 
 5     public FileSystemClassLoader(String rootDir) 
 6         this.rootDir = rootDir;
 7     
 8 
 9     protected Class<?> findClass(String name) throws ClassNotFoundException 
10         byte[] classData = getClassData(name);
11         if (classData == null) 
12             throw new ClassNotFoundException();
13          else 
14             return defineClass(name, classData, 0, classData.length);
15         
16     
17 
18     private byte[] getClassData(String className) 
19         String path = classNameToPath(className);
20         try 
21             InputStream ins = new FileInputStream(path);
22             ByteArrayOutputStream baos = new ByteArrayOutputStream();
23             int bufferSize = 4096;
24             byte[] buffer = new byte[bufferSize];
25             int bytesNumRead;
26             while ((bytesNumRead = ins.read(buffer)) != -1) 
27                 baos.write(buffer, 0, bytesNumRead);
28             
29             return baos.toByteArray();
30          catch (IOException e) 
31             e.printStackTrace();
32         
33         return null;
34     
35 
36     private String classNameToPath(String className) 
37         return rootDir + File.separatorChar
38                 + className.replace(‘.‘, File.separatorChar) + ".class";
39     
40 

 

以上是关于JVM系列四:类加载的主要内容,如果未能解决你的问题,请参考以下文章

JVM系列二(类加载)

JVM系列二(类加载)

Android 逆向类加载器 ClassLoader ( 类加载器源码简介 | BaseDexClassLoader | DexClassLoader | PathClassLoader )(代码片段

JVM总括四-类加载过程双亲委派模型对象实例化过程

JVM总括四-类加载过程双亲委派模型对象实例化

JVM系列 - JVM类加载机制详解