1. 类加载初始化

  1. Loading

    把一个class文件加载到内存

  2. Linking

    1. Verification

      校验class文件符不合乎class文件规范

    2. Preparation

      动态变量赋默认值 static int count = 10;

      在这一步count = 0 默认值

    3. Resolution

      符号援用转换为内存地址 能够间接拜访的地址

  3. Initializing

    动态变量赋值为初始值

2. 类加载器

1. JVM是按需动静加载采纳双亲委派机制

用getClassLoader获取类加载器 如果是Null 那就是到了BootStrap类加载器 了 因为是C++实现的 木有对应类

咱们平时写的类 就再classpath下 所以是由App类加载器加载的

留神: 这四个类加载器没有继承关系 只是程序关系

父加载器不是父类,只是说本人加载不了了,就交给上一级加载

他们不是继承关系 那是怎么产生分割? 用组合啦~~

2. 步骤1图是不是不能阐明双亲委派? 我给你一张图 让你明确一下双亲委派

不 ! 我为什么要用图? 我先用一句话形容:classLoader在本人的cache缓存中找是不是曾经加载过类XX,如果没有就找他的父类加载器,父类加载器在本人的cache缓存中找是不是曾经加载过类XX,找到就返回,找不到就接着找父类加载器,直到BootStrap 如果最初还没在cache中找到 那就从父类加载器往回找,父加载器问儿子你看看你负责的jar包什么的 有没有这个类,有的话你就加载到内存,没有的话你就找你儿子类加载器去看,始终看看看看,看到最初如果有返回最小的类加载器,还是没找见 那就抛异样吧 ClassNotFoundException

我还是屈从了,上图吧。

/** * @author 木子的昼夜 */public class MyClassLoaderTest {    public static void main(String[] args) throws ClassNotFoundException {        MyClassLoaderTest.class.getClassLoader().loadClass("classtest.MyClassLoaderTest");    }}// ClassLoader#loadClasspublic Class<?> loadClass(String name) throws ClassNotFoundException {        return loadClass(name, false);} 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;     } }

设计模式:钩子函数 模板办法

3. 一个class文件被load到内存里是什么样

4. 想晓得某个类是被哪个类加载器加载到内存里的 getClassLoader()

System.out.println(String.class.getClassLoader());// null --> BootStrap类加载器System.out.println(sun.awt.HKSCS.class.getClassLoader());// null --> BootStrap类加载器       System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());// sun.misc.Launcher$ExtClassLoader@1d44bcfa --> ExtSystem.out.println(ClassLoaderTest.class.getClassLoader());// sun.misc.Launcher$AppClassLoader@18b4aac2--> App

5. 为啥搞双亲委派

  1. 为了平安 如果没有双亲委派 间接从自定义类加载器找class (次要问题)

(1) 如果这里我自定义一个java.lang.String

      我在String类里边写一段挖矿脚本

(2) 而后小月月下载了我的类库 筹备应用 !

他用到了String ,他认为这是jdk的类,后果我预判了他的预判,这个String类曾经是我的了,由我掌控

(3) 双亲委派就能够防止这个问题,因为零碎始终会先去上一层看有没有这个类 因为上一层有了 所以就不 会应用我定义的String类了

  1. 如果父层加载了 我就不必加载了 再加载就是浪费资源了 (主要问题)

6. 既然自定义了 不是我说什么就是什么吗 ?

不是! 类加载器曾经限度死了

7. 类加载器父子关系示例 不是继承 是组合

看下边代码,读者能够一试 再认真品一品 这句话: 加载类加载器的类加载器 不是类加载器的父类加载器

System.out.println(ParentDemo.class.getClassLoader());System.out.println(ParentDemo.class.getClassLoader().getClass().getClassLoader());System.out.println(ParentDemo.class.getClassLoader().getParent());System.out.println(ParentDemo.class.getClassLoader().getParent().getParent());输入后果:sun.misc.Launcher$AppClassLoader@18b4aac2nullsun.misc.Launcher$ExtClassLoader@6e0be858null

8. 各个类加载器范畴

能够看一眼 sun.misc.Launcher 源码

  • BootstrapClassLoader : sun.boot.class.path
  • ExtensionClassLoader: java.ext.dirs
  • AppClassLoader:java.class.path
 public static void main(String[] args) {     System.out.println("---------BOOT-----------------------");     String boot = System.getProperty("sun.boot.class.path");     Arrays.stream(boot.split(";")).forEach(s-> System.out.println(s));     System.out.println("--------------Ext-------------------");     String ext = System.getProperty("java.ext.dirs");     Arrays.stream(ext.split(";")).forEach(s-> System.out.println(s));     System.out.println("----------APP-----------------------");     String app = System.getProperty("java.class.path");     Arrays.stream(app.split(";")).forEach(s-> System.out.println(s));}输入后果:---------BOOT-----------------------C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\sunrsasign.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jarC:\Program Files\Java\jdk1.8.0_144\jre\classes--------------Ext-------------------C:\Program Files\Java\jdk1.8.0_144\jre\lib\extC:\WINDOWS\Sun\Java\lib\ext----------APP-----------------------C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jarC:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jarE:\workspace_idea\blogmaven\target\classes // 读者留神看这一个  这个是以后我的项目class文件地址    

9. 自定义类加载器

  1. 先定一个一个被加载的java类 Test.java

    public class Test {    public void say(){        System.out.println("胜利喽!");    }}
  2. 编译Test.java 到指定目录

    javac  -encoding utf-8  -d E:/classes  Test.java 
  3. 继承ClassLoader 、 重写模板办法findClass 、调用defineClass

    /** * @author 木子的昼夜 */public class MyClassLoader extends ClassLoader  {    @Override    protected Class<?> findClass(String name) throws ClassNotFoundException {        FileInputStream inputStream = null;        ByteArrayOutputStream out = null;        try {            // 类名  aa.xx 转换为 aa/xx.class            File file = new File("E:\\classes\\",name.replaceAll("\\.","/").concat(".class"));            // 获取输出流             inputStream = new FileInputStream(file);            // 输入字节流             out = new ByteArrayOutputStream();            // 转换为字节数组            int available = inputStream.available();            byte[] byteArr = new byte[available];            inputStream.read(byteArr);            // 生成class             return defineClass(name,byteArr,0,byteArr.length);        }catch (Exception e) {            throw  new ClassNotFoundException();        }finally {            // 敞开流            if (inputStream != null) {                try {                    inputStream.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (out != null) {                try {                    out.close();                } catch (IOException e) {                    e.printStackTrace();                            }            }        }    }}
  4. 测试

    public static void main(String[] args) {        try {            Class<?> clazz = new MyClassLoader().loadClass("Test");            // 获取申明的办法            Method say = clazz.getDeclaredMethod("say");            // 创立实例            Object instance = clazz.newInstance();            // 调用办法            say.invoke(instance);        }catch (Exception e) {            e.printStackTrace();        }}输入后果:    胜利喽!
  5. 自定义类加载器加载自加密的class

    1. 文件是你本人的,你在编译完了之后能够通过某种算法加密一下,
      等你用自定义ClassLoader加载的时候,你能够本人个儿解密
    2. 能够避免反编译
    3. 能够避免被篡改

10. 混合编译

  1. 解释器 : bytecode intepreter
  2. JIT: Just In - Time compiler

             1.  -Xmixed 默认为混合模式      开始应用解释执行 启动速度快     对热点代码进行实时检测和编译         2.  -Xint 应用解释模式 启动快 执行慢         3.  -Xcomp 应用纯编译模式 启动慢 运行快 
  3. 混合模式

    1. 解释器+热点代码编译2. 起始阶段采纳解释器3. 热点代码检测 检测到了应用热点代码编译        1.  屡次被调用的办法(办法计数器:监测办法执行频率)               2.  屡次被调用的循环(循环计数器:监测循环执行频率)   

未完 待续...

本文有很多图,如果不分明的话,大家能够关注公众号:木子的昼夜

发送"类加载" 即可取得高清图拜访地址

发送"路线" 即可取得本系列文章纲要

也可发送本人想问的问题给我,我会在看到的第一工夫回复

最初附上本人公众号刚开始写 愿一起提高:

留神: 以上文字 仅代表个人观点,仅供参考,如有问题还请指出,立刻马上连滚带爬的从被窝里进去改过。