您的位置:首页 > 其它

深入理解JVM-类加载

2018-01-31 08:57 155 查看
看完《深入理解JVM》中关于类加载的一些个人总结,欢迎评论!

 

类加载过程:

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。它们开始的顺序如下图所示:

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

 

其中类加载过程:加载、验证、准备、解析、初始化

加载、验证、准备、初始化发生的顺序是确定的,而解析阶段不一定;

在某些情况可以在初始化阶段之后开始,以支持java的动态绑定

(因为类中的方法可能是动态绑定)

 

java绑定:绑定是指把一个方法的调用与方法所在的类关联;分为以下两种方式:

静态绑定:在程序执行前方法已经被绑定,java当中的方法只有final,static,private和够早方法是前期半丁

动态绑定:运行时根据具体对象的类型进行绑定,java中所有的方法都是后期绑定;

 

 

加载:

加载阶段,虚拟机需要完成三件事:

1.通过一个类的全限定名来获取其定义的二进制字节流;

2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;

3.在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区中这些数据的访问入口;

 

类加载器:

类加载器不止实现类的加载动作,

启动类加载器,扩展类加载器,应用程序类加载器,自定义加载器;

如果程序中没有自定义类加载器,默认的就是应用程序类加载器;

 

 

双亲委派模型:

每一次上面的类加载器叫做当前层类加载器的父加载器;使用组合的方式调用父加载器中的代码;

双亲委派工作流程:

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。

 

好处:Java类随着它的类加载器一起具备了带有优先级的层次关系;一个类只会被一个加载器加载,保证java程序的稳定运行;

 

 

验证:

  验证的目的是为了确保Class文件中的字节流包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身安全;

  大致都会完成以下四个阶段的验证:

  文件格式的验证,元数据验证,字节码验证,符号引用验证;

 

 

准备:

  准备阶段是正式为类变量分配内存并设置类变量初始的阶段,这些内存都将在方法区中分配;

  需注意:

这时候进行内存分配的仅包括类变量(static),而不包括示例变量,实例变量会在对象实例化时随着对象一块分配在java堆中
这里所设置的初始值同城都是数据类型的默认值,而不是显示地赋予的值;
如果同时被static和final修饰,那么在准备阶段就会赋值
 

解析:

     解析阶段是虚拟机将常量池中的符号引用转化为直接引用的过程;

类或接口的解析:判断所要转化成的直接引用是对数组类型,还是普通的对象类型的引用,从而进行不同的解析;
字段解析:是对字段进行解析,会在本类中查找是否包含有简单名称和字段描述都与目标相匹配的字段,如果有则查找结束,没有则会按继承关系从上往下递归搜索该类所实现的各个接口和它们的父接口;
类方法解析:对类方法的解析与字段步骤差不多,只是多了判断该方法所处的类还是接口的步骤,而且对类方法的匹配搜索,先搜索父类,再搜索接口;
接口方法解析:递归向上搜索父接口就行了;
 

初始化:

     初始化是类加载最后一步,此阶段才真正开始执行类中定义的java代码。在准备阶段,类变量已经赋过一次系统初始值,初始化阶段则根据程序的主观计划去初始化类变量和其他资源;初始化是执行类构造器<clinit>方法的过程;

     clinit方法是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,静态语句块只能访问到定义在静态语句块之前的变量,对于定义在静态语句块之后的,只能赋值,不能访问;

     不需要显示地调用父类构造器,虚拟机保证子类的clinit执行之前父类的clinit已经执行完毕。

     如果类中没有静态语句块也没有对类的赋值操作,JVM可以不为这个类生成clinit方法;

     执行接口的clinit方法不需要先执行父接口的clinit,只有当父接口中定义的变量呗使用时,父接口才会被初始化;接口实现类初始化时也如此;

     虚拟机会保证clinit在多线程环境中被正确的加锁和同步;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: