JVM基础课一:类加载

橙三吉。 提交于 2020-01-11 18:10:11

JVM

  1. JVM虚拟机,可以看作软件模拟完整的硬件系统。
  2. 解释执行.class的字节码文件,在java中是通过javac命令来编译.java代码为字节码文件的
  3. javap xxx.class 可以查看字节码文件
  4. jvm上运行的应用程序都有一个特点,一次编写,到处运行,应用不在对底层操作系统,物理设置有依赖,而只依赖jvm,并且JVM还有自动内存管理,对多线程友好等特点。
  5. jdk源码:http://jdk.java.net/java-se-ri/7。

字节码文件

  1. 字节码的指令集有不超过256条指令,大多数和类型相关。
  2. 例如,invokevirtual指调用对象的实例方法,会根据对象的实际类型进行分派,在运行期间确定,也称为动态分派。

类加载

  1. 类加载是指JVM加载.class的字节码文件到内存(元数据区,方法区)的过程,在java程序中主要涉及Class对象的建立,静态变量分配内存空间和赋值,static代码块的执行。
  2. JVM对类加载是按需加载的,当执行new操作,static变量和方法使用,反射调用,Class.forName("…"),getClassLoader().loadClass()等操作时会触发类加载过程;
  3. 通过在启动参数中添加-verbose:class,-verbose:gc,-verbose:jni可以查看启动类加载,gc和本地方法调用的日志。

类加载的过程

  1. 类加载过程分为:加载,验证,准备,解析,初始化,使用,卸载几个阶段,阶段之间有一定的重叠运行。
  2. 加载:.class文件(或者动态生成的字节码)转换成二进制流,静态内容转换方法区数据,生成Class对象作为方法区该类的访问入口,这个阶段开发人员可控性较强。
  3. 验证:验证.class二进制流是否符号JVM要求,如果是反复使用,信任的java代码,可以通过-Xverify:none来关闭大部分验证。
  4. 准备:方法区分配内存给类变量,并赋初值(零值),static final修饰的常量也完成了内存分配。
  5. 解析:常量池的部分符号引用替换为直接引用,链接到方法区的其他类,解析时间不固定,可以在类加载就解析或者在使用时才解析。
  6. 符号引用是指对类,字段,方法的引用,其中对类的解析需要加载类,并判断访问权限,对字段的解析:在自身类,接口类,父类中寻找该字段,对类方法的解析:跟字段的解析类似,寻找该方法;
  7. 初始化:执行cinit(),由静态变量赋值和静态代码块组合而成,默认会调用父类的cinit();
  8. 因为类只会加载一次,所以静态变量,静态代码块只会执行一次,并且new SuperClass[10],并不会初始化SuperClass。

类加载器

  1. 负责类加载的加载过程,即获取字节码的二进制流,应用可控性强;
  2. 加载过程采用双亲委派的方式,即将类加载器分层级,加载某个类会逐层向上委派父类加载器来加载,只有当父类加载器无法加载时才会让子类加载器尝试加载。这种方式保证一个类只会被一个类加载加载;
  3. 采用双亲委派是因为类是由自身和其加载器共同确定其唯一性,因此要避免同一个类被不同加载器加载到;并且类之间也规定只能访问自身类加载器和上级类加载器加载的类,不能向下访问,这样做的好处是保证了类的安全性,父类加载器加载的类不会对子类加载器加载的类有依赖性;
  4. jvm对类加载器的分层,最上层是启动类加载器,负责加载JAVA_HOME/lib目录下指定名称的jar(rt.jar,charsets.jar);然后是扩展类加载器,负责加载JAVA_HOME/lib/ext目录下的jar;然后是系统类加载器,负责加载classPath路径下的jar和class文件。
获取系统类加载器:AppClassLoader:ClassLoader.getSystemClassLoader()
获取classpath:this.getClass().getClassLoader().getResource("").toString()  ??
  1. 源码上类加载器ClassLoader是通过loadClass() --》 findClass() --》defineClass()实现的,入口是loadClass(),内部调用findClass(),findClass()内部再调用defineClass()(将流转成class实例)然后返回,其中loadClass()保证了双亲委派,通常构造一个类加载器只需要重写findClass即可。
实现自定义类加载器MyClassLoader:
D:/SimpleCustom.class:
public class SimpleCustom {
	public int age = 30;
	static {
        System.out.println("SimpleCustom loaded");
        System.out.println(SimpleCustom.class.getClassLoader());//输出类装载器的类型
    }
}
MyClassLoader:
public class MyClassLoader extends ClassLoader {
public MyClassLoader(){  }
// 从文件中读取,形成字节码类  
    @Override  
    protected Class<?> findClass(String name) throws ClassNotFoundException {  
        try {  
            //将class文件进行解密  
            FileInputStream fis = new FileInputStream("D:/SimpleCustom.class");  
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] byteArr = new byte[1024];
            int len;
            while((len = fis.read(byteArr)) != -1){
            	bos.write(byteArr, 0, len);
            }
            byte[] classByte = bos.toByteArray(); 
            //将字节流变成一个class 
            Class<?> c = this.defineClass(null, classByte, 0, classByte.length);
            return c;
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return super.findClass(name);  
    }  
}
public class SimpleApp {
	static{
		System.out.println("SimpleApp loaded");
        System.out.println(SimpleApp.class.getClassLoader());//输出类装载器的类型
	}
	public static void main(String[] args) {
		try{
            ClassLoader loader = new MyClassLoader();
            Class<?> c = loader.loadClass("SimpleCustom");
			// 注意直接用sc.age是访问不到的,因为SimpleCustom是由自定义类加载器加载的,而SimpleApp是系统类加载器加载的,父级别类加载器加载的类是无法访问子类加载器加载的类,但可以通过反射访问
            System.out.println(c.getDeclaredField("age").get(c.newInstance()));
		}catch(Exception e){
		}
	}
}
SimpleApp loaded
sun.misc.Launcher$AppClassLoader@439a8942
SimpleCustom loaded
com.dengh.classLoader.MyClassLoader@1558473e
30
  1. 也存在双亲委派不适合的场景,如,jdbc中父加载器加载的代码需要使用各个厂商的实现,而厂商的实现只能由子类加载器来加载。另外还有OSGi,支持热插拔的模块化,已经形成业界标准;

  2. 类加载在热部署上的应用:通过每次创建新的类加载器实例来加载类,实现一个类修改后的重复加载,同时丢弃上次加载的类加载器实例,这样上次加载的类也被丢弃了,但注意丢弃的类加载器实例会被回收,但是生成的Class对象是存放在元数据区的,如果有大量热部署,可能导致内存溢出;

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!