java虚拟机学习笔记二:浅谈虚拟机的类加载执行子系统机制

时间:2022-06-10 21:16:03

一概述:

    java程序设计语言之所以能够占据软件开发的大部分市场 并且长久不衰,其中一个很大的原因就是因为它的平台无关性,而实现这一重要特性的核心则是java虚拟机.但是其实设计java虚拟机的开发人员在一开始就没有打算让虚拟机只可以运行允许由java语言编写的程序.也就是说其实java虚拟机不仅仅要实现平台无关性,还要实现语言的无关性.而无论是跨平台,还是跨语言.虚拟机和class字节码文件都是基础.以我现在的理解虚拟机其实真正所绑定的并不是java(这里指的是由javac编译器编译形成的.class也就是我们常说的java类),而是字节码文件.所以对于虚拟机而言,.class文件才是它数据的输入源.

二Class字节码文件:

    对于这部分理解比较浅薄,因为Class字节码文件的结构有很多的基础性知识,在没有掌握精这些基础知识时是很难去理解它的结构.所以这里只是简要的谈谈我对于它的理解.Class字节码文件作为虚拟机的数据来源,虚拟机当然要对其结构进行规范,虚拟机的类加载不会去加载不符合Class字节码文件规范的字节码文件.其实class字节码文件在没有编译前就是我们所写的java源文件(如果是其他语言就是其他语言的源文件)所以Class字节码文件的结构是由我们原来的源文件加上编译时增加的内容组成的.这里面说一下我理解到的.

(1)关于jdk 的兼容性:在Class字节码文件的头部分会有一个标识这个标识当前编译器的JDK版本.java虚拟机规范中提到java虚拟机对Class文件中JDK标识版本高于当前java虚拟机所属的JDK版本的Class字节码拒绝执行类加载操作..比如:现在我们用JDK6.0编译形成的Class字节码文件的头部分就会有一个jDK的标识号这个标识号应该是50(具体为什么是50书上写有).如果我们现在的java虚拟机是JDK7.0或者JDK6.0的话则可以对这个Class字节码文件进行类加载动作.但是如果是JDK5.0无论你这个Class文件是否规范java虚拟机都会拒绝去加载它.所以说jDK是向下兼容向上不兼容的.


三类加载:

   java虚拟机的类加载执行子系统按照顺序可以分为5部分:

   加载--->验证--->准备--->解析--->初始化

   每一个步骤都对应着相应的操作,并且这5个步骤虽然是顺序执行的但是他们却是交替执行的步骤,即上一个步骤激活了下一个步骤后,下一个步骤可能在上一个步骤在执行的过程中代替它交替执行.其中验证,准备,解析可以整合在一起称为连接

   按照类加载的5个步骤来说我们的java类的生命周期也就显而易见了

    加载--->验证--->准备--->解析--->初始化--->使用--->卸载

四类加载中的类加载器:  

    如果是Class字节码文件是虚拟机的输入源,那么虚拟机中的类加载执行子系统中的类加载器就是负责将输入源(字节码数据)加载到虚拟机进程中的工具.虚拟机中描述类加载器工具时这么说到" 通过一个类的全限定名称(包名加类名)来获取此类的二进制字节码 "这段话有2层意思一就是我们既可以从磁盘中获取class字节码文件来得到二进制字节码,也可以从网络或者其他地方获取到二就是这个类加载器可以由java虚拟机外部来实现(当然虚拟机内部也可以自己动态生成字节码数据)其实在类加载执行子系统中可以由虚拟机外部也就是我们程序来决定的东西实在太少而类加载器就是其中之一.

        (1)  java系统默认提供了3个类加载器:

                bootstrap(启动类加载器):  它用来加载JAVA_HOME/lib下的类库注意这个JAVA_HOME并不是指我们配置的环境变量,具体JAVA_HOME的路径由谁来决定我现在也没搞清楚,但是可以通过System中的getproperty获取KeyJAVA_HOME对应的路径.通常启动类加载器用来加载rt.jar等也就是java的系统类库.并且这个类加载器是有本地方法不是由java实现的.注意通常bootstrap累加器加载的JAVA_HOME路径是jdk/jre/lib/

               ExtClassLoader(扩展了加载器): 它用来加载JAVA_HOME/lib/ext目录中的类库,这个类加载器使用java编写的并且他还是个内部类sun.misc.Launcher$ExtClassloader是它的全限定名称可以看出来它是Launcher类的内部类实现

               AppClassLoader(系统/应用程序类加载器): 通常我们自己编写的应用程序的类库和引入的Jar包类库都是由这个类加载器加载的.所以这个类加载器加载的目录就是我们常说的Classpath路径下的类库.这个类加载器也是由java编写的并且他也是Launcher类的内部类sun.misc.Launcher$AppClassLoader并且这个类加载器也是线程默认的类加载器实现同时也是Classloader.getSystemClassloader的默认实现

               ClassLoader(类加载器抽象类):这个类很抽象就如它是一个抽象类一样他提供了由java编写的类加载器(系统提供的类加载和自定义类加载器)应该实现的类加载方法,但是很多都没有实现.

         (2) 类加载器的委派机制

                Java的类加载器在加载的时候遵循着一种委派机制,遵循这种机制的好处就是可以防止类被重复加载而防止了重复加载就可以节约内存并且避免加载重复类导致程序应用混乱.搞清楚委派机制之前先要明白一个问题.

                Java的类在虚拟机中存放的时候并不是靠类的包名加类名这种组合形式来做唯一标识的而是由加载它的类加载器和包名加类名这样的形式存放并且确定唯一标识的这里很像IP地址由网络号和主机号组成.所以类加载不仅仅是一个用来加载类的工具同时还是类的容器..那委派机制究竟如何做的呢?Java类加载器在加载一个类的时候通常会取出当前类的包名+类名在自己的的容器内(其实就是由自己加载的类集合中)查找是否加载过这个类,如果有则直接返回没有那么当前类加载器不会主动去加载它而是委托到上层类加载器那么上层类加载做同样的处理后如果最上层类加载发现要加载的类路径它无法获取到那么才会由本身的类加载器来加载.这样做每一个类在加载的时候都会在所以类加载上轮询一遍找到它真正的"宿主".当然我们自定义类加载器也可以打破这种委派机制