jvm2 classloader的过程
类的加载过程
- loading 将class文件load到内存
- verification
- preparation 将静态变量赋默认值
- resolution 常量池里面用到的符号引用转换为内存地址,可直接访问到
- initialize 静态变量赋初始值 调用静态代码块
类加载器
- class文件被load到内存后,会生成一个class对象,该对象指向这个class文件。该对象应该在metaspace.
System.out.println(String.class.getClassLoader());
System.out.println(sun.awt.HKSCS.class.getClassLoader());
System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());
System.out.println(T002_ClassLoaderLevel.class.getClassLoader());
System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader().getClass().getClassLoader());
System.out.println(T002_ClassLoaderLevel.class.getClassLoader().getClass().getClassLoader());
System.out.println(new T006_MSBClassLoader().getParent());
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(sun.net.spi.);
System.out.println(T002_ClassLoaderLevel.class.getClassLoader().getClass().getClassLoader());
- Bootstrap > Extension > App > Custom 子父加载器不是继承关系 也不是类加载器的加载器 他们的加载器都是Bootstrap 父加载器指的是该加载器类内部parent的对象
例如ApplicationClassLoader中的Parent为ExtensionClassloader
- 一个class文件load过程 双亲委派!
如果自定义了classloader 自定义加载器内部有一个缓存 如果没找到该class 则去父类加载器去找 (先向上找该类加载了没有)
找到bootstrap后 该加载器没有找到 则开始向下委托Extention>App>CustomerClassLoader(再向下依次委托找加载器去加载该类,如果该加载器不负责加载该类则交由子加载器加载)
如果CustomerLoader还是没有加载成功(class文件不存在) 则报ClassnotFoundException
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先检查这个classsh是否已经加载过了
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// c==null表示没有加载,如果有父类的加载器则让父类加载器加载
if (parent != null) {
c = parent.loadClass(name, false); //父类递归
} else {
//如果父类的加载器为空 则说明递归到bootStrapClassloader了
//bootStrapClassloader比较特殊无法通过get获取
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {}
if (c == null) {
//如果bootstrapClassLoader 仍然没有加载过,则递归回来,尝试自己去加载class
long t1 = System.nanoTime();
c = findClass(name); //findClass实现需要子类~!!! 所以自定义ClassLoader只需重写findClass即可
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
- 为什么需要双亲委派呢?
1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全.
- 自定义类加载器
public class T005_LoadClassByHand {
public static void main(String[] args) throws ClassNotFoundException {
Class clazz = T005_LoadClassByHand.class.getClassLoader().loadClass("com.mashibing.jvm.c2_classloader.T002_ClassLoaderLevel");
System.out.println(clazz.getName());
//利用类加载器加载资源
//T005_LoadClassByHand.class.getClassLoader().getResourceAsStream("");
}
}
自定义classloader 继承classload而 重写findClass即可
public class T006_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);//将class二进制流转换成class对象!!!
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name); //throws ClassNotFoundException
}
public static void main(String[] args) throws Exception {
ClassLoader l = new T006_MSBClassLoader();
Class clazz = l.loadClass("com.mashibing.jvm.Hello");////这个也是双亲委派下 先向上找是否加载过,再向下找加哪个可以加载 而app并不可以加载该类文件,因为路径不对!!!
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());//是applicationclassloader
System.out.println(l.getParent());
System.out.println(getSystemClassLoader());
}
}
classloader中的findClass方法
lazyloading
jvm规范没有规定何时加载
但严格规定了什么时候必须初始化
- new getstatic putstatic invokestatic等指令 访问final变量除外
- reflect对类进行反射
- 初始化子类的时候 父类必须首先初始化
- 虚机启动时 主类必须初始化
混合模式
混合使用解释器+热点代码编译
起始阶段采用解释执行
热点代码检测
- 多次被执行的方法 检测方法执行频率
- 多次被调用的循环 检测循环执行频率
- 进行编译
为什么不直接编译执行
- -Xmixed
- -Xint 纯解释模式 启动快 执行慢 bytecode intepreter
- -Xcomp 纯编译模式 启动慢 执行快 jit just in time compile