1. 类加载的生命周期?
Java中的类加载器负责将字节码文件加载到JVM中,并创建对应的Class对象。类的加载过程主要包括以下几个阶段:
- 加载(Loading):查找并加载类的二进制数据。当程序需要使用某个类时,如果该类还未被加载到内存中,系统会通过ClassLoader查找并加载该类的二进制数据,生成对应的Class对象。
- 验证(Verification):验证类的二进制数据的正确性。主要包括文件格式、元数据、字节码和符号引用等方面的验证。
- 准备(Preparation):为类的静态变量分配内存并设置默认初始值。JVM会为类的静态变量分配内存,并根据变量类型设置默认初始值,例如,整型变量默认值为0,对象引用变量默认值为null等。
- 解析(Resolution):将符号引用转换为直接引用。在这个阶段,JVM会将类、接口、字段、方法等符号引用转换为在内存中的直接引用。
- 初始化(Initialization):执行类的初始化操作。在这个阶段,JVM会执行类构造器(方法)的代码,为静态变量赋值,执行静态代码块等操作。注意:只有在该类被主动使用时才会触发初始化操作,例如:创建类的实例、调用类的静态变量或方法、使用反射访问类等。
- 使用(Using):类的正常使用阶段。
- 卸载(Unloading):卸载类的Class对象和类在JVM中占用的内存空间。当JVM不再需要某个类时,ClassLoader会卸载该类并释放相关内存空间。
2. 类加载器的层次?
Java类加载器的层级结构如下:
Bootstrap ClassLoader
|
Extension ClassLoader
|
System ClassLoader
|
Custom ClassLoader
- BootStrap ClassLoader:是最顶层的类加载器,它是虚拟机的内部实现,并不是一个普通的Java类。它用于加载Java平台核心库,比如rt.jar等,一般情况下是由JVM实现并且不会暴露出来。
- Extension ClassLoader:用于加载Java平台的扩展库,一般是jre/lib/ext目录下的jar包。
- System ClassLoader:也称为Application ClassLoader,它用于加载应用程序classpath目录下的类,是最常用的类加载器。
- Custom ClassLoader:用户自定义的类加载器,它可以用于加载任意位置的类,比如网络、数据库等。
3. Class.forName()和ClassLoader.loadClass()区别?
区别如下:
- Class.forName()方法会返回一个已经初始化(static块已经执行)的Class对象,而ClassLoader.loadClass()方法只会返回一个Class对象的引用,并不会执行类的初始化操作。
- Class.forName()方法除了加载类之外,还会执行该类的静态块(如果有的话),并且可以指定是否要初始化该类,而ClassLoader.loadClass()方法仅仅是加载类,并不会执行类的任何代码,也不能指定是否要初始化该类。
- Class.forName()方法可以传入类的全限定名(例如com.example.MyClass),也可以传入类的Class对象,而ClassLoader.loadClass()方法只能传入类的全限定名。
- Class.forName()方法可以指定类加载器,如果不指定,它将使用当前线程的上下文类加载器(ContextClassLoader),而ClassLoader.loadClass()方法必须要指定类加载器。
4. JVM有哪些类加载机制?
- 前台加载(Bootstrap ClassLoader):该类加载器加载JVM核心类库,即JVM内部必须使用的类,如java.lang.Object和java.lang.ClassLoader等。由JVM自带,一般开发者无法自定义。
- 扩展加载(Extension ClassLoader):该类加载器加载位于JRE扩展目录中的类库,一般位于jre/lib/ext目录下。由sun.misc.Launcher$ExtClassLoader实现,可以通过系统属性java.ext.dirs来指定扩展目录。
- 系统加载(System ClassLoader):也称为应用程序类加载器(Application ClassLoader),它负责加载CLASSPATH环境变量或-classpath选项指定路径下的类库,是最常用的类加载器,所有由该加载器加载的类都具有相同的类加载器。由sun.misc.Launcher$AppClassLoader实现。
- 用户自定义加载器(Custom ClassLoader):开发者可以自定义类加载器,来实现特定的加载需求,比如从非标准位置加载类文件,从数据库或网络加载类文件等。自定义类加载器必须继承自java.lang.ClassLoader类或其子类,然后覆盖其中的相关方法,实现自己的加载逻辑。常见的用户自定义加载器有URLClassLoader和JarClassLoader等。