- Loading
-
混合执行 编译执行 解释执行
-
检测热点代码:-XX:CompileThreshold = 10000
-
-
自定义类加载器
-
extends ClassLoader
-
overwrite findClass() -> defineClass(byte[] -> Class clazz)
-
-
ClassLoader的源码
-
findInCache -> parent.loadClass -> findClass()
-
-
双亲委派,主要出于安全来考虑
-
Linking
-
Verification
-
验证文件是否符合JVM规定
-
-
Preparation
-
静态成员变量赋默认值
-
-
Resolution
-
将类、方法、属性等符号引用解析为直接引用 常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
-
-
-
Initializing
-
调用类初始化代码,给静态成员变量赋初始值
-
-
-
小总结:
-
load - 默认值 - 初始值
-
new - 申请内存 - 默认值 - 初始值
-
类加载器为什么使用双亲委派:主要是为了安全,如果自定义类加载器,然后自己封装一个java.lang.String包来覆盖原有的,当调用java.lang.String中的方法就不安全,可以获取你输入的内容;‘
类加载器,把磁盘中.class二进制文件加载到内存中,并生成一个Class对象,指向那块内存地址,类加载器加载原理使用双亲委派,从下层类加载器查找类,如果没有,就调用父类加载器,类似于迭代,一层层向上查找,当没有找到就一层层返回然后根据传递的路径进行加载。
类加载器与反射一样,插拔式开发,如果有一个模块想要随时替换,就可以使用反射或者使用类加载器加载,使用这种方式是程序运行以后加载,不是编译阶段加载,可以绕过编译的验证环节。
Class<?> clazz = Demo01ApplicationTests.class.getClassLoader().loadClass("类的路径");
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; } }
重写类加载器,集成ClassLoader类重新findClass方法就可以
public class MSBClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File f = new File("c:/test/", name.replace(".", "/").concat(".class"));
try {
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while ((b=fis.read()) !=0) {
baos.write(b);
}
byte[] bytes = baos.toByteArray();
baos.close();
fis.close();//可以写的更加严谨
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name); //throws ClassNotFoundException
}
public static void main(String[] args) throws Exception {
ClassLoader l = new MSBClassLoader();
Class clazz = l.loadClass("com.mashibing.jvm.Hello");
Class clazz1 = l.loadClass("com.mashibing.jvm.Hello");
System.out.println(clazz == clazz1);
Hello h = (Hello)clazz.newInstance();//反射
h.m();
System.out.println(l.getClass().getClassLoader());
System.out.println(l.getParent());
System.out.println(getSystemClassLoader());
}
}
java的执行:解释执行、编译执行(win系统中生成exe本地运行文件),当一个方法被频繁调用,JIT会把这段代码编译执行生成exe提高效率,所以有方法计数器,循环计数器当数值大于阈值就会编译执行,默认是混合模式执行的。
热部署:继承ClassLoad重写loadClass方法,而不是重写findClass,findClass打破不了双亲委派的机制,所以热部署失败,不会重新加载
对象的结构