zoukankan      html  css  js  c++  java
  • JVM 自定义类加载器在复杂类情况下的运行分析

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

    1、使用之前创建的类加载器

    public class MyTest16  extends  ClassLoader{
    
        private String className;
    
        //目录
         private String path;
    
        private final String fileExtension = ".class";
    
        public MyTest16(String classLoadName){
            super(); //将系统类加载器当做该类加载器的父加载器
            this.className = classLoadName;
        }
    
        public MyTest16(ClassLoader parent, String classLoadName){
            super(parent); //显示指定该类加载器的父加载器器
            this.className = classLoadName;
        }
    
        public void setPath(String path) {
            this.path = path;
        }
    
        @Override
        public String toString() {
            return "[" + this.className + "]";
        }
    
        @Override
        protected Class<?> findClass(String clasName) throws ClassNotFoundException {
            System.out.println("findClass invoked:" + clasName);
            System.out.println("class loader name: " + this.className);
            byte[] data = this.loadClassData(clasName);
            return  this.defineClass(clasName,data, 0, data.length);
        }
    
        private byte[] loadClassData(String className){
            InputStream is = null;
            byte[] data = null;
            ByteArrayOutputStream baos = null;
    
            try{
                className = className.replace(".","//");
                //System.out.println("className:" +this.className);
                is = new FileInputStream(new File(this.path + className + this.fileExtension));
                baos = new ByteArrayOutputStream();
                int ch = 0;
                while ( -1 != (ch = is.read())){
                    baos.write(ch);
                }
                data = baos.toByteArray();
    
            }catch (Exception ex){
                ex.printStackTrace();
            }finally {
                try {
                    is.close();
                    baos.close();
                }catch (Exception ex){
                    ex.printStackTrace();
                }
            }
    
            return  data;
    
        }
    
       
    
    
    }
    

      

    2、创建MyCat类

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

      

    3、创建MySample类

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

      

    4、创建测试类

    public class MyTest17 {
        public static void main(String[] args)  throws  Exception{
            MyTest16 loader1 = new MyTest16("loader1");
    
            Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MySample");
    
            System.out.println("class:" + clazz.hashCode());
    
            //如果注释掉改行,那么并不会实例化MySample对象,即MySample构造方法不会被调用
            //因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加重MyCat Class
            Object object = clazz.newInstance();
    
    
    
        }
    }
    

      

    打印结果

    class:1735600054
    MySample is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    

      

    增加-XX:+TraceClassLoading后的打印结果

     

    如果去掉Object object = clazz.newInstance();

    打印结果为

    说明:如果注释掉Object object = clazz.newInstance();该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用

             因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat Class。

             而且这个例子说明MyCat没有被预先加载

    二、对一的Code进行改造

    在一的基础上,新建一个测试类

    public class MyTest17_1 {
        public static void main(String[] args)  throws  Exception{
            MyTest16 loader1 = new MyTest16("loader1");
            loader1.setPath("D:/temp/");
            Class<?> clazz = loader1.loadClass("com.example.jvm.classloader.MySample");
    
            System.out.println("class:" + clazz.hashCode());
    
            //如果注释掉该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用
            //因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat Class
            Object object = clazz.newInstance();
    
    
    
        }
    }
    

      里面增加了一个方法loader1.setPath("D:/temp/");

    然后将MyCat.class 和MySample.class 剪切到D:/temp/目录下,如下面两图

    打印结果:

    findClass invoked:com.example.jvm.classloader.MySample
    class loader name: loader1
    class:2133927002
    MySample is loaded by:[loader1]
    findClass invoked:com.example.jvm.classloader.MyCat
    class loader name: loader1
    MyCat is loaded by:[loader1]
    

      

    三、类加载命名空间

    在二的基础上,MyCat.java 增加一行代码System.out.println("from MyCat:" + MySample.class);

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

      然后重新build,打印结果如下:

    class:1735600054
    MySample is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    from MyCat:class com.example.jvm.classloader.MySample
    

      

    现在将build路径下的classloader文件夹拷贝到D: empcomexamplejvmclassloader下,删除build路径下的MySample.class

    打印输出结果:

    findClass invoked:com.example.jvm.classloader.MySample
    class loader name: loader1
    class:2133927002
    MySample is loaded by:[loader1]
    MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
    Exception in thread "main" java.lang.NoClassDefFoundError: com/example/jvm/classloader/MySample
    	at com.example.jvm.classloader.MyCat.<init>(MyCat.java:10)
    	at com.example.jvm.classloader.MySample.<init>(MySample.java:10)
    	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:423)
    	at java.lang.Class.newInstance(Class.java:442)
    	at com.example.jvm.classloader.MyTest17_1.main(MyTest17_1.java:16)
    Caused by: java.lang.ClassNotFoundException: com.example.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:335)
    	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    	... 8 more
    

      说明:MySample由自定义loader加载,MyCat由AppClassLoader。两个类是不同的loader加载,处于不同的命名空间。

         所以System.out.println("from MyCat:" + MySample.class); 抛出异常。 子加载器所加载的类能够访问父加载器所加载的类。反之,父加载器所加载的类无法访问子加载器所加载的类

      

  • 相关阅读:
    15. DML, DDL, LOGON 触发器
    5. 跟踪标记 (Trace Flag) 834, 845 对内存页行为的影响
    4. 跟踪标记 (Trace Flag) 610 对索引组织表(IOT)最小化日志
    14. 类似正则表达式的字符处理问题
    01. SELECT显示和PRINT打印超长的字符
    3. 跟踪标记 (Trace Flag) 1204, 1222 抓取死锁信息
    2. 跟踪标记 (Trace Flag) 3604, 3605 输出DBCC命令结果
    1. 跟踪标记 (Trace Flag) 1117, 1118 文件增长及空间分配方式
    0. 跟踪标记 (Trace Flag) 简介
    SpringBoot + Redis + Shiro 实现权限管理(转)
  • 原文地址:https://www.cnblogs.com/linlf03/p/11027293.html
Copyright © 2011-2022 走看看