Java 虚拟机类加载机制和字节码执行引擎

我们知道Java代码编译后生成的是字节码,那虚拟机是如何加载这些class字节码文件的呢?加载之后又是如何进行方法调用的呢?

一 类文件结构 无关性基石

Java有一个口号叫做一次编写,到处运行。实现这个口号的就是可以运行在不同平台上的虚拟机和与平台无关的字节码。这里要注意的是,虚拟机也是中立的,只要是符合规范的字节码,都可以被虚拟机接受,例如Groovy,JRuby等语言,都会生成符合规范的字节码,然后被虚拟机所运行,虚拟机不关心字节码由哪种语言生成。

类文件结构

class类文件是一组以8位字节为基础的二进制流,它包含以下几个部分:

魔数和class文件版本:类文件开头的四个字节被定义为CAFEBABE,只有开头为CAFEBABE的文件才可以被虚拟机接受,接下来四个字节为class文件的版本号,高版本JDK可以兼容以前版本的class文件,但不能运行以后版本的class文件。
常量池:可以理解为class文件中的资源仓库,它包含两大类常量:字面量和符号引用,字面量包含文本字符串,声明为final的常量值等,符号引用包含类和接口的全限定名,字段的名称和描述符,方法的名称和描述符。
访问标志:常量池结束后,紧接着两个字节表示访问标志,用于识别一些类或接口层次的访问信息,例如是否是public,是否是static等。
类索引,父类索引,和接口索引集合:类索引用来确定这个类的全限定名,父类为父类的全限定名,接口索引集合为接口的全限定名。
字段表集合:用于描述接口或者类中声明的变量,但不包含方法中的变量。
方法表集合:用于表述接口或者类中的方法。
属性表集合:class文件,字段表,方法表中的属性都源自这里。

二 类加载机制 虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换分析和初始化,最终形成可以被虚拟节直接使用的JAVA类型,这就是虚拟机的类加载机制。

类从被加载到虚拟机内存到卸载出内存的生命周期包括:加载->连接(验证->准备->解析)->初始化->使用->卸载。

初始化的5种情况:

使用new关键字实例化对象时,读取或设置一个类的静态字段,除被final修饰经编译结果放在常量池的静态字段,调用类的静态方法时。

使用java.lang.reflect包方法对类进行反射调用时。(Class.forName())。

初始化子类时,如果父类没有初始化。

虚拟机启动时main方法所在的类。

当使用JDK1.7动态语言支持时,java.lang.invoke.MethodHandle实例解析结果为REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,且对应类没有进行初始化。

类加载过程

加载
加载是类加载的第一个阶段,虚拟机要完成以下三个过程:

通过类的全限定名获取定义此类的二进制字节流。

将字节流的存储结构转化为方法区的运行时结构。

在内存中生成一个代表该类的Class对象,作为方法区各种数据的访问入口。

验证
目的是确保class文件字节流信息符合虚拟机的要求。

准备
为static修饰的变量赋初值,例如int型默认为0,boolean默认为false。

解析
虚拟机将常量池内的符号引用替换成直接引用。

初始化
初始化是类加载的最后一个阶段,将执行类构造器< init>()方法,注意这里的方法不是构造方法。该方法将会显式调用父类构造器,接下来按照java语句顺序为类变量和静态语句块赋值。

类加载器

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在java虚拟机中的唯一性。举一个例子:

package com.sinaapp.gavinzhang.bean; import java.io.InputStream; public class App { public static void main( String[] args ) { ClassLoader myClassLoader = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try{ String fileName = name.substring(name.lastIndexOf(".")+1)+".class"; InputStream is = getClass().getResourceAsStream(fileName); if(is == null) { System.out.println(fileName+ "is not find"); return super.loadClass(name); } System.out.println("fileName: "+fileName); byte[] b = new byte[is.available()]; is.read(b); return defineClass(name,b,0,b.length); }catch (Exception E) { throw new ClassCastException(name); } } }; try { Object obj = myClassLoader.loadClass("com.sinaapp.gavinzhang.bean.Resource").newInstance(); Object obj1 = Class.forName("com.sinaapp.gavinzhang.bean.Resource").newInstance(); System.out.println(obj instanceof com.sinaapp.gavinzhang.wesound.bean.Resource); System.out.println(obj1 instanceof com.sinaapp.gavinzhang.wesound.bean.Resource); }catch (Exception e) { e.printStackTrace(); } } }

结果为:

101847_3jDH_1983603.png

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/64ce876c46ba63b786635be8ffb409c2.html