JVM-类加载和初始化
JVM-类加载和初始化
类加载器
loading
jvm中所有的class都是被classloader加载到内存
以上几个类加载器的关系不是继承,是父加载器与自加载器的关系。
双亲委派
- 父加载器
父加载器不是“类加载器的加载器” - 双亲委派是一个孩子向父亲方向,然后父亲向孩子方向的双亲委派过程
那么问题来了, 为什么要搞双亲委派
java.lang.String类由自定义加载器加载行不行?
回答这个问题, 首先要弄明白class的加载过程。
根据上图所示,一个class类首先要经过CustomClassloader(自定义类加载器),查询其缓存中是否已经将该class加载,如果有,则将其返回,没有,则向上检查,此时到了APP(ApplicationClassLoader,同样检查其缓存是否已加载,没有,则继续向上,Extension加载器同样如此,一直检查到BootStrap加载器,当Bootstrap加载器同样没有加载该calss时,开始自顶向下进行实际查找和加载。首先判断该类是否该由Bootstrap加载,不是,则向下,一直到Custom加载器,如果没有找到,则抛异常(ClassNotFound)。
主要是为了安全
假设自定义了一个java.lang.String,覆盖sun的String,同时自定义一个String的类加载器,将自定义的这个String加载到内存,接下来将整个自定义部分打包成一个类库,交给客户使用 ,此时客户输入密码将会变得非常不安全。
但是采用双亲委派就不会有这个问题,自低向上检查,一直到Bootstap,发现String类已经被Bootstrap加载,其他加载器便不能再次加载这个类,从而保证了安全。
类加载过程
类加载器范围
查看每个目录下都有哪些jar包
public class T003_ClassLoaderScope {
public static void main(String[] args) {
String pathBoot = System.getProperty("sun.boot.class.path");
System.out.println(pathBoot.replaceAll(";", System.lineseparator()));
System.out.println("--------------------");
String pathExt = System.getProperty("java.ext.dirs");
System.out.println(pathExt.replaceAll(";", System.lineseparator()));
System.out.println("--------------------");
String pathApp = System.getProperty("java.class.path");
System.out.println(pathApp.replaceAll(";", System.lineseparator()));
}
}
输出结果
C:\Program Files\Java\jdk1.8.0_51\jre\lib\resources.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\sunrsasign.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jsse.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jce.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\charsets.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jfr.jar
C:\Program Files\Java\jdk1.8.0_51\jre\classes
--------------------
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext
C:\Windows\Sun\Java\lib\ext
--------------------
C:\Program Files\Java\jdk1.8.0_51\jre\lib\charsets.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\deploy.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\access-bridge-64.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\cldrdata.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\dnsns.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\jaccess.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\jfxrt.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\localedata.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\nashorn.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunec.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunjce_provider.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunmscapi.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\sunpkcs11.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\zipfs.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\javaws.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jce.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jfr.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jfxswt.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\jsse.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\management-agent.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\plugin.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\resources.jar
C:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar
G:\SoftWare\Java\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar
自定义类加载器
lazyloading
public class T008_LazyLoading { //严格讲应该叫lazy initialzing,因为java虚拟机规范并没有严格规定什么时候必须loading,但严格规定了什么时候initialzing
public static void main(String[] args) throws Exception {
P p;
X x = new X();
System.out.println(P.i);
System.out.println(P.j);
Class.forName("com.cyc.jvm.c2_classloader.T008_LazyLoading$P");
}
public static class P {
final static int i = 8;
static int j = 9;
static {
System.out.println("P");
}
}
public static class X extends P {
static {
System.out.println("X");
}
}
}
混合模式
测试
package com.cyc.jvm.c2_classloader;
public class T009_WayToRun {
public static void main(String[] args) {
for(int i=0; i<10_0000; i++)
m();
long start = System.currentTimeMillis();
for(int i=0; i<10_0000; i++) {
m();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
public static void m() {
for(long i=0; i<10_0000L; i++) {
long j = i%3;
}
}
}
首先是混合模式,这也是默认的运行模式
混合模式运行时间
解释模式
查看运行时间(时间过于漫长)
编译模式
查看运行时间
总结
- 加载过程
-
Loading
-
双亲委派,主要出于安全来考虑
-
LazyLoading 五种情况
-
ClassLoader的源码
- findInCache -> parent.loadClass -> findClass()
-
自定义类加载器
- extends ClassLoader
- overwrite findClass() -> defineClass(byte[] -> Class clazz)
- 加密
- 第一节课遗留问题:parent是如何指定的,打破双亲委派,学生问题桌面图片
- 用super(parent)指定
- 双亲委派的打破
- 如何打破:重写loadClass()
- 何时打破过?
- JDK1.2之前,自定义ClassLoader都必须重写loadClass()
- threadcontextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
- 热启动,热部署
- osgi tomcat 都有自己的模块指定classloader(可以加载同一类库的不同版本)
-
混合执行 编译执行 解释执行
- 检测热点代码:-XX:CompileThreshold = 10000
-
-
Linking
-
Initializing
-
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。