07_JVM学习笔记_类加载情况的运行分析

自定义类加载器在复杂类加载情况下的运行分析

示例代码:

package com.leofight.jvm.classloader;

public class MyCat {

    public MyCat() {
        System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());
    }
}

package com.leofight.jvm.classloader;

public class MySample {

    public MySample() {
        System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());

        new MyCat();
    }
}

package com.leofight.jvm.classloader;

public class MyTest17 {

    public static void main(String[] args) throws Exception {

        MyTest16 loader1 = new MyTest16("loader1");

        Class clazz = loader1.loadClass("com.leofight.jvm.classloader.MySample");
        System.out.println("class: " + clazz.hashCode());

        Object object = clazz.newInstance();
    }
}

该程序运行结果会输出什么?

输出

class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc

上述类并非自定义加载器来加载的,而是由系统类加载器所加载。

修改代码如下:


package com.leofight.jvm.classloader;

public class MyTest17 {

    public static void main(String[] args) throws Exception {

        MyTest16 loader1 = new MyTest16("loader1");

        Class clazz = loader1.loadClass("com.leofight.jvm.classloader.MySample");
        System.out.println("class: " + clazz.hashCode());

        //Object object = clazz.newInstance();
    }
}

输出结果会是什么?

输出

class: 1627674070

小结
如果注释掉该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用。因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat Class

加上-XX:+TraceClassLoading查看详细的类加载情况

未注释掉Object object = clazz.newInstance();的加载情况

......
[Loaded com.leofight.jvm.classloader.MyTest16 from file:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes/]
[Loaded com.leofight.jvm.classloader.MySample from file:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes/]
class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
[Loaded com.leofight.jvm.classloader.MyCat from file:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes/]
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
[Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar]

注释掉Object object = clazz.newInstance();的加载情况

......
[Loaded java.lang.Void from /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded com.leofight.jvm.classloader.MyTest16 from file:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes/]
[Loaded com.leofight.jvm.classloader.MySample from file:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes/]
class: 1627674070
[Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar]

类加载器命名空间实战剖析与透彻理解

示例代码:

package com.leofight.jvm.classloader;

public class MyTest17_1 {

    public static void main(String[] args) throws Exception {

        MyTest16 loader1 = new MyTest16("loader1");
        loader1.setPath("/Users/Desktop/");

        Class clazz = loader1.loadClass("com.leofight.jvm.classloader.MySample");
        System.out.println("class: " + clazz.hashCode());

        Object object = clazz.newInstance();
    }
}

运行上述程序,查看结果

输出

class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc

拷贝存放.class文件的整个目录到桌面,并运行,查看结果
输出

class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc

结果一样,原因:在当前的classpath仍然存在MySample、MyCat,系统加载器仍可找到。现在删除classpath下的整个两个class文件,查看运行结果。

输出:

findClass invoked: com.leofight.jvm.classloader.MySample
class loader name: loader1
class: 1625635731
MySample is loaded by:[class com.leofight.jvm.classloader.MyTest16@1627674070]
findClass invoked: com.leofight.jvm.classloader.MyCat
class loader name: loader1
MyCat is loaded by:[class com.leofight.jvm.classloader.MyTest16@1627674070]


现在由自定义类加载器所加载。

继续修改
重新build后删除MyCat,仅留MySample,桌面上这两个类的class文件都在,运行该程序会是什么结果?

输出:

class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
Exception in thread "main" java.lang.NoClassDefFoundError: com/leofight/jvm/classloader/MyCat
    at com.leofight.jvm.classloader.MySample.(MySample.java:8)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at java.lang.Class.newInstance(Class.java:442)
    at com.leofight.jvm.classloader.MyTest17_1.main(MyTest17_1.java:20)
Caused by: java.lang.ClassNotFoundException: com.leofight.jvm.classloader.MyCat
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 7 more

为什么会出错?
MySample是由系统加载器所加载,MyCat是由加载了MySample的类加载器来加载,因为classpathMyCat的class文件不存在,所以报错。

继续修改
重新build,删除MySample,留下MyCat的class,接下来的输出结果。

输出:

findClass invoked: com.leofight.jvm.classloader.MySample
class loader name: loader1
class: 1625635731
MySample is loaded by:[class com.leofight.jvm.classloader.MyTest16@1627674070]
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc

为什么可以正常运行?
MySample是由自定义类加载器所加载,MyCat是由加载了MySample的类加载器来加载,因为桌面MySample、MyCat class字节码文件都存在,所以该程序正常运行。

修改MyCat如下:加入MySample的引用

package com.leofight.jvm.classloader;

public class MyCat {

    public MyCat() {
        System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());

        System.out.println("from MyCat: " + MySample.class);
    }
}

重新build,执行结果如下:

输出

class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
from MyCat: class com.leofight.jvm.classloader.MySample

复制classpath下的所有class文件到桌面,删除classpath下的MySample,然后运行,结果如下:

输出:

findClass invoked: com.leofight.jvm.classloader.MySample
class loader name: loader1
class: 1625635731
MySample is loaded by:[class com.leofight.jvm.classloader.MyTest16@1627674070]
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
Exception in thread "main" java.lang.NoClassDefFoundError: com/leofight/jvm/classloader/MySample
    at com.leofight.jvm.classloader.MyCat.(MyCat.java:8)
    at com.leofight.jvm.classloader.MySample.(MySample.java:8)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at java.lang.Class.newInstance(Class.java:442)
    at com.leofight.jvm.classloader.MyTest17_1.main(MyTest17_1.java:20)
Caused by: java.lang.ClassNotFoundException: com.leofight.jvm.classloader.MySample
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 8 more

为什么报错?
MySample与MyCat是由不同的类加载器所加载,所以命名空间不同,不可以互相访问。

修改MySample如下:加入MyCat的引用

package com.leofight.jvm.classloader;

public class MySample {

    public MySample() {
        System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());

        new MyCat();

        System.out.println("from MySample " + MyCat.class);
    }
}

修改MyCat,去除MySample的引用

package com.leofight.jvm.classloader;

public class MyCat {

    public MyCat() {
        System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());

        //System.out.println("from MyCat: " + MySample.class);
    }
}

重新build,复制classpath下的所有class文件到桌面,删除classpath下的MySample,然后运行,结果如下:

findClass invoked: com.leofight.jvm.classloader.MySample
class loader name: loader1
class: 1625635731
MySample is loaded by:[class com.leofight.jvm.classloader.MyTest16@1627674070]
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
from MySample class com.leofight.jvm.classloader.MyCat

小结
关于命名空间的重要说明
1.子加载器所加载的类能够访问父加载器所加载的类
2.父加载器所加载的类无法访问到子加载器所加载的类

类加载器实战剖析与疑难点解析

示例代码

package com.leofight.jvm.classloader;

public class MyTest18 {
    public static void main(String[] args) {
        System.out.println(System.getProperty("sun.boot.class.path"));
        System.out.println(System.getProperty("java.ext.dirs"));
        System.out.println(System.getProperty("java.class.path"));
    }
}

输出:

/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/classes
/Users/leofight/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/tools.jar:/Users/lz/IdeaProjects/jvm_lecture/out/production/classes:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar

示例代码

package com.leofight.jvm.classloader;

public class MyTest18_1 {

    public static void main(String[] args) throws Exception {
        MyTest16 loader1 = new MyTest16("loader1");

        loader1.setPath("/Users/leofight/Desktop/");

        Class clazz = loader1.loadClass("com.leofight.jvm.classloader.MyTest1");

        System.out.println("class: " + clazz.hashCode());
        System.out.println("class loader:" + clazz.getClassLoader());

    }
}

运行输出

class: 1627674070
class loader:sun.misc.Launcher$AppClassLoader@14dad5dc

执行sudo cp -r ~/IdeaProjects/jvm_lecture/out/production/classes/com classes,然后运行,输出

class: 1639705018
class loader:null

示例代码

package com.leofight.jvm.classloader;

import com.sun.crypto.provider.AESKeyGenerator;

public class MyTest19 {

    public static void main(String[] args) {
        AESKeyGenerator aesKeyGenerator = new AESKeyGenerator();

        System.out.println(aesKeyGenerator.getClass().getClassLoader());
        System.out.println(MyTest19.class.getClassLoader());
    }
}

输出

sun.misc.Launcher$ExtClassLoader@2b193f2d
sun.misc.Launcher$AppClassLoader@14dad5dc

执行java -Djava.ext.dirs=./ com.leofight.jvm.classloader.MyTest19

输出

Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/crypto/provider/AESKeyGenerator
        at com.leofight.jvm.classloader.MyTest19.main(MyTest19.java:8)
Caused by: java.lang.ClassNotFoundException: com.sun.crypto.provider.AESKeyGenerator
        at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 1 more

示例代码

package com.leofight.jvm.classloader;

public class MyPerson {

    private MyPerson myPerson;

    public void setMyPerson(Object object) {
        this.myPerson = (MyPerson) object;
    }
}

package com.leofight.jvm.classloader;

import java.lang.reflect.Method;

public class MyTest20 {
    public static void main(String[] args) throws Exception {
        MyTest16 loader1 = new MyTest16("loader1");
        MyTest16 loader2 = new MyTest16("loader2");

        Class clazz1 = loader1.loadClass("com.leofight.jvm.classloader.MyPerson");
        Class clazz2 = loader2.loadClass("com.leofight.jvm.classloader.MyPerson");

        System.out.println(clazz1 == clazz2);

        Object object1 = clazz1.newInstance();
        Object object2 = clazz2.newInstance();

        Method method = clazz1.getMethod("setMyPerson", Object.class);
        method.invoke(object1, object2);
    }
}

输出

true

你可能感兴趣的:(07_JVM学习笔记_类加载情况的运行分析)