loading:
将类文件加载到内存,在堆中生成一个Class类对象供外部调用
linking:
验证(verification):验证,确保类信息没安全方面问题
准备(preparation):赋默认值
解析(resolution):符号引用变真实地址值
initializing
执行类构造器、赋初始值、执行静态代码块
如果父类没有初始化,会先初始化父类
对于类:load + 默认值 + 初始值
对于对象:new + 申请内存 + 默认值 + 初始值
java是混合执行,即解释执行和编译执行。-Xint纯解释,-Xmixed混合执行,-Xcomp纯编译
检测热点代码-XX:CompileThreshold=1000,超过这个值的调用混合模式会编译。JIT:Just In-Time Complier
Bootstrap(启动类加载器,加载核心类)->Extenstion(扩展类加载器,加载扩展类)->Application(应用程序类加载器,自己写的代码)->Custom(自定义类加载器)
从下往上找是否被加载过,再从上往下加载。找不到同时也无法加载则报ClassNotFoundException
是有类缓存的
为什么要用双亲委派?
1.为了保证核心类库的安全,避免出现用户自定义java.lang.Object这样的情况。
2.有一个缓存。
代理模式即将指定类的加载交给其他类加载器。双亲委托机制是代理模式的一种
可以对class文件加密,然后用自己的类加载器加密后加载
加载指定类,找到或生成对应的字节代码。即java.lang.Class的一个实例。还可以加载一些资源文件
getParent()返回父类加载器
loadClass(String name)加载名为name的类
findClass(String name)查找名name类
findLoadedClass(String name)查找已被加载过的类
defineClass(String name,byte[] b,int off, int len)把字节数组b中的内容转换成java类,返回类实例
resolveClass连接指定的java类
继承ClassLoader,重写findClass()方法
1.首先检查请求类型是否已经装载,如果装载则返回
2.委派给父类加载器,如果父类能够找到或装载完成则返回
3.调用本类加载器的findClass方法,获取到则defineClass导入到方法区
重写loadClass即可打破
线程上下文类加载器
当前线程类加载器就是为了抛弃双亲委派加载链模式
ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定。Thread.currentThead().getContextClassLoader()
热部署,热启动:tomcat先加载再查找,为了保证安全不查找核心库。
package at.guigu.study.jvm;
import sun.misc.PerfCounter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;
public class CustomClassLoaderTest extends ClassLoader {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
CustomClassLoaderTest loaderTest = new CustomClassLoaderTest();
Class<?> aClass = loaderTest.loadClass("at.guigu.study.jvm.CustomClassLoaderTest");
aClass.getMethod("hello").invoke(aClass.newInstance(), new Object[]{});
// 这里可以看出,新加载的类,并不会影响之前的。
loaderTest.hello();
}
public String hello(){
// class文件里是输出jvm,上面运行中先输出了jvm,再输出了hello
System.out.println("hello");
return "hello";
}
@Override
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 {
// 重写了loadClass方法,意味着java.lang包中类也会通过这个加载,所以要用extClassLoader加载。这里用app的父类加载器
Class<?> aClass = Thread.currentThread().getContextClassLoader().getParent().loadClass(name);
if (aClass != null) {
return aClass;
}
} 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
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File file = new File("D:\\project\\java\\study_java\\file\\CustomClassLoaderTest.class");
try {
FileInputStream fileInputStream = new FileInputStream(file);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int b = 0;
while ((b = fileInputStream.read()) != -1) {
byteArrayOutputStream.write(b);
}
byte[] bytes = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.close();
fileInputStream.close();
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
}
// class文件
package at.guigu.study.jvm;
public class CustomClassLoaderTest {
public CustomClassLoaderTest() {
}
public String hello() {
System.out.println("jvm");
return "hello";
}
}
verification验证是否符合jvm规范和安全
perparation静态成员变量赋默认值
resolution
将类、方法、属性等符号引用解析为直接引用
常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用