JVM学习笔记1

类加载器ClassLoader

ClassLoader本身是一个类,是由启动类加载器来加载的(启动类加载器属于JVM内置的,不属于java实现,根据不同的虚拟机实现而实现,可能是C语言等)

加载类的过程

通过给定一个binary name:

  • “java.lang.String”的字符串;
  • ““com.jvm.Test i n n e r C l a s s ” 代 表 T e s t 类 的 内 部 类 i n n e r C l a s s , 中 间 用 innerClass”代表Test类的内部类innerClass,中间用 innerClassTestinnerClass隔开”;
  • “com.csf.Test i n n e r C l a s s innerClass innerClassChild1$1” 代表Test的内部类innerClass的内部类Child的第一个内部类;
  • “com.csf.Test$3$1” 代表Test的第三个内部类的第一个内部类;

ClassLoader会尝试根据给定的name去定位该资源(本身存在的类)或者尝试生成类的数据(运行期动态生成的类)
类加载到虚拟机内存中经历的几个阶段包括:
加载:将类的二进制数据加载进内存(方法区中),然后生成这个类的Class对象,
作为方法区中这个类的各种资源的访问入口(类似一个镜像),JVM虚拟机并没有固定该对象存放在哪,Class对象比较特殊,在HotSpot虚拟机而言,它存放在方法区中
每一个Class对象都会保留加载它的类加载器的引用,即可以通过
Class.getClassLoader()获取到加载该类的类加载器
其中:数组类的Class对象并不是类加载器加载生成的,而是由JVM虚拟机运行期自动创建的,其中非原生类型的数据getClassLoader返回的是加载数组元素的类加载器,而原生类型并没有类加载器,返回null
JVM虚拟机并没有规定这个二进制数据从哪里来的,所以有很多种方式:

  • 从压缩文件读取,常见的就是jar包依赖

  • 从网络获取,applet技术

  • 动态生成(Spring动态代理)

  • 其他文件获取(JSP技术)

    连接:
    1、验证:对于加载进来的字节码文件进行必要的验证,比如魔数验证(JDK版本),class文件格式验证,class文件内容验证等等
    2、准备:为类的静态变量分配内存,并赋予一个初始值
    3、解析:将类的符号引用解析为直接引用
    初始化:对类的静态变量赋予正确的初始值

类的初始化时机

类的初始化需要类被主动使用,JVM虚拟机规范规定的有且仅有五种,类才会被初始化,其他情形都不能触发类的初始化:
1 遇到new、getstatic、putstatic、invokestatic字节码指令(对应的new对象、获取对象静态属性,赋值类的静态属性,调用类的静态方法)
2、使用java.lang.reflect包对类进行反射调用
3、当初始化一个类时,他的父类没有被初始化,则必须初始化其父类
4、指定为程序入口的那个类
5、当使用JDK1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析 结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的 类没有被初始化时,则必须进行初始化
当调用的是类的常量,即final static修饰的变量时,并不会触发类的初始化,java编译期在编译期间会执行很多优化操作,这里就是常量优化,即把被调用的类的常量编译到自己的类的常量池中,使用时直接使用本类的那个常量
6、注意:**当类没有被主动使用时,即使类加载过程出现了ERROR错误,JVM虚拟机也不能进行异常提示,除非用户主动去使用该类,否则加载类出错,虚拟机也不会抛出异常**

如何去寻找类

在oracle的jvm中使用的是经典的双亲委托模型(该模型是JVM虚拟机规范推荐的,但不是强制的),当自己尝试去加载类时,会委托给他的父类去加载,当父类无法加载时,才有本身去尝试加载
loadclass这个方法用来去加载类:

  • 它是一个同步方法
  • 他首先会调用findLoadedclass方法区检查类是否被加载过了,没有?
  • 调用父类的loadClass方法去尝试加载,如果没有父类,则会调用启动类加载器尝试加载
  • 父类加载失败,就是调用自身的findClass方法去尝试加载
    当用户需要自定义类加载器时,需要实现的就是这个findClass方法,如何找到需要加载的类,以下是个人示例,仅供参考
protected Class findClass(String className)  throws ClassNotFoundException{
        byte[] data =  this.loadClassData(className);
        System.out.println("findClass invoke class:"+className);
        System.out.println("findClass invoke classLoadName:"+classLoaderName);
        return super.defineClass(className,data,0,data.length);
    }
    /**
     * 自定义class的二进制数据从哪里获取,写一个用户自定义的ClassLoader类,唯一需要实现的是,
     * 指定class数据的获取方式,获取到class 的二进制数据
     * @param binaryName
     * @return
     */
    private byte[] loadClassData(String binaryName ) {
        InputStream is = null;
        byte[] bytes = null ;
        ByteArrayOutputStream bos = null;
        binaryName = binaryName.substring(binaryName.lastIndexOf(".")+1);
        try{
            is = new FileInputStream(new File(this.path+binaryName +this.finaNameExtession ));
            bos = new ByteArrayOutputStream();
            int length ;
            while ( -1 != (length = is.read())){
                bos.write(length);
            }
            bytes = bos.toByteArray();
        }catch ( Exception e ){
            e.printStackTrace();
        } finally {
            try {
                if ( null != is )
                    is.close();
                if ( null != bos )
                    bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }

你可能感兴趣的:(JVM类加载)