类加载时机
new、getstatic、putstatic、invokestatic命令初始化类时
reflect包反射调用类时
初始化类时,父类未初始化,要先初始化父类
启动Java程序时需要指定主类(Main),虚拟机会执行该类的初始化
类加载过程
加载->验证->准备->解析->初始化->使用->卸载
加载:通过全限定类型和对应的classpath及Java环境变量目录,将二进制class文件加载到方法区
初始化:初始化创建字节码class对象
类加载器

Bootstrap ClassLoader(启动类加载器):加载JAVA_HOME\lib目录或-Xbootclasspath指定的虚拟机认可的类
Extension ClassLoader(扩展类加载器):加载JAVA_HOME\lib\ext目录或java.ext.dirs指定的类库
Application ClassLoader(应用类加载器):负责加载classpath目录下的类库
User ClassLoader(用户自定义类加载):加载应用外的类文件,e.g.JRebel
自定义类加载器案例
继承ClassLoader抽象类后,因历史原因需要兼容,所以提供重写loadClass、findClass来实现指定类文件加载,建议重写findclass方法
public class CustomClassLoader extends ClassLoader {
private String classpath;
public CustomClassLoader(String classpath) {
this.classpath = classpath;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundExc String path = classpath + File.separator + name.replace(".", File.separator) + ".class";
try {
InputStream is = new FileInputStream(path);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] bytes = new byte[2048];
int len = 0;
while ((len = is.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
byte[] classData = bos.toByteArray();
return defineClass(name, classData, 0, classData.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return super.loadClass(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String path = classpath + File.separator + name.replace(".", File.separator) + ".class";
try {
InputStream is = new FileInputStream(path);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] bytes = new byte[2048];
int len = 0;
while ((len = is.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
byte[] classData = bos.toByteArray();
return defineClass(name, classData, 0, classData.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}
}
class CustomClassLoaderTest {
@Test
void findClass() throws Exception {
CustomClassLoader customClassLoader = new CustomClassLoader("C:\\Users\\80604\\Desktop");
Class<?> c = customClassLoader.loadClass("com.zhaoccf.study.jvm.classloader.Test");
if (c!=null){
Object obj = c.newInstance();
Method method = c.getMethod("say");
method.invoke(obj,null);
System.out.println(obj.getClass().getClassLoader());
System.out.println(c.getClassLoader());
}
}
@Test
void loadClass() throws Exception {
CustomClassLoader customClassLoader = new CustomClassLoader("C:\\Users\\80604\\Desktop");
Class<?> c = customClassLoader.loadClass("java.lang.String");
if (c!=null){
Object obj = c.newInstance();
Method method = c.getMethod("say");
method.invoke(obj,null);
System.out.println(obj.getClass().getClassLoader());
System.out.println(c.getClassLoader());
}
}
}
执行结果
// findClass
Hello CustomClassLoader!
com.zhaoccf.study.jvm.classloader.CustomClassLoader@7ff2a664
com.zhaoccf.study.jvm.classloader.CustomClassLoader@7ff2a664
Process finished with exit code 0
//loadclass
java.lang.NoSuchMethodException: java.lang.String.say()
at java.lang.Class.getMethod(Class.java:1786)
at com.zhaoccf.study.jvm.classloader.CustomClassLoaderTest.loadClass(CustomClassLoaderTest.java:30)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Process finished with exit code -1
双亲委派与打破双亲委派
当类加载器收到加载任务时,优先交给父类加载器去完成,只有当父类加载器无法完成加载时子类才会尝试加载该类
// 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 {
//如果父类加载器为空则启动类加载器加载,启动类加载器不是Java实现所以为null
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原始核心类
避免重复加载,父类加载过的子类无需加载
为什么打破双亲委派?
某些情况下,父类需要加载的class文件受加载范围限制,无法加载,只能委托子类加载器进行加载
怎么打破双亲委派?
- Java SPI
ServiceLoader内部类LazyIterator加载META-INF/services/类全限定类名文件

java.sql.Driver在rt.jar里应该由启动类加载器来加载,现在由ServiceLoader存储在ContextClassLoader里的应用类加载器来加载对应厂商的实现
public class DriverLoad {
public static void main(String[] args) {
ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
Iterator<Driver> iterator = loader.iterator();
if (iterator.hasNext()) {
Driver driver = iterator.next();
System.out.println("driver:"+driver.getClass()+"classloader:"+driver.getClass().getClassLoader());
}
System.out.println("thread context loader:"+Thread.currentThread().getContextClassLoader());
System.out.println("serviceloader:"+ServiceLoader.class.getClassLoader());
}
}
执行结果
driver:class com.mysql.jdbc.Driverclassloader:sun.misc.Launcher$AppClassLoader@18b4aac2
thread context loader:sun.misc.Launcher$AppClassLoader@18b4aac2
serviceloader:null
Process finished with exit code 0
Dubbo增强SPI
Tomcat加载多个应用程序
热加载技术(devtools、JRebel、OSGi等)