3.JVM类加载机制

(1)类加载机制
虚拟机把描述类的数据从Class文件,用ClassLoader ,加载到内存,并对数据进行校验、转换解析和初始化,最终形成虚拟机直接使用的java类型,
这就是虚拟机的类加载机制。

(2)在java语言里面,类型的加载、连接、初始化过程都是在程序运行期间完成的,
缺点:增加性能开销
优点:提高程序灵活性

(3)类的生命周期
a.加载
b.连接(包括:验证,准备,解析)
c.初始化
d.使用
e.卸载(出内存)
类的加载过程必须按照这五个步骤,按部就班的 开始 。

c.初始化
什么情况下,会开始加载?JVM规范并没有强制性约束,但是有五种情况,必须立刻对类进行初始化(加载连接必然要在初始化之前开始)

I. 遇到,new , getstatic , putstatic , invokestatic四条字节码指令时,如果没有进行过初始化,则需要先触发其初始化
这四条字节码指令使用场景:
new: 实例化对象
getstatic: 读取一个类的静态变量
putstatic: 设置一个类的静态变量
invokestatic: 调用一个类的静态方法的时候

II.使用java.lang.reflect包,对类进行反射调用的时候,如果类没有过初始化,则对其初始化。

III.当初始化一个类的时候,发现其父类还没有过初始化,需要初始化

IV.JVM启动时,main()方法所在的类

V.当使用JDK1.7的动态语言支持时,方法句柄对应的类没有过初始化,则对其初始化

(Test3继承Test2,当调用Test3的静态变量时,先初始化Test3; Test2也有静态变量a,Test3继承Test2 , 调用Test3.a时候,
只会初始化Test2,不会初始化Test3 , 至于会不会加载和连接Test3 ,取决于JVM的具体实现,Sun HotSpot虚拟机,
可通过XX:+TraceClassLoading观察到,此操作会导致子类的加载 )

Test2 [] test = new Test2[10]; 不会初始化Test2

c是Test的一个常量,在常量池里面,所以,调用Test2.c,也不会初始化Test2

当一个类引用了另一个类的常量时,编译时,会把被引用的常量,转化为引用者自己常量池中的常量,所以,编译完后,两个类就没有关系了

(4)类加载过程
a. 加载 ,是类加载(Class Loading)过程中的一个阶段。三个阶段:
I.通过一个类的全限定名,来获取此类的二进制字节流。
II.将字节流所代表的静态存储结构,转化为方法区的运行时数据结构。
III.在内存中生成一个代表这个类java.lang.Class对象,作为方法区这个类的各种数据访问入口。
(数组不是类加载器创建,是由JVM自己创建的。
对于HotSpot虚拟机而言,Class对象比较特殊,它虽然是对象,但是,存放在方法区里面。这个对象将作为,程序访问方法区中数据的外部接口。


b.连接
I.验证:确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机。
这个阶段是否严禁,直接决定了java虚拟机能否承受恶意代码的攻击。
只有通过了验证,字节流才会存入方法区,所以验证早于加载的存储。

II.准备:准备阶段是正式为类变量(static),分配内存,并设置变量的初始值,这些变量所使用的内存都将在方法区进行分配。
第一,这个时候进行内存分配的仅仅是类变量(static),不包括实例变量。实例变量将会在对象初始化时候分配在堆中。
第二,这里所说的初始值,通常情况下是类型0值,即public static int a = 123 , 此时只为a分配a=0。
第三,如果指定了public final static int a = 123,此时,为a初始化a=123。

III.解析:解析阶段是,虚拟机将常量池内的符号引用,替换为直接引用的过程。
解析动作主要针对 类、接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。

c.初始化 , 初始化是类加载过程的最后一步,初始化阶段,才真正开始执行,类中定义的java程序代码。
I. 初始化,是执行类构造器clinit()方法的过程,而非类的构造方法。
II. client()方法,是编译器收集类中所有类变量(static) 的赋值动作,和静态方法块中的语句合并产生的。
编译器收集的顺序,是由语句在源文件中的顺序决定的。
III.静态语句块中,只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态块中可以赋值,但是不能访问。
static{
i = 0;//可以执行
System.out.print(i);// 会提示非法向前引用
static int i =1;
}
public Class Parent{
public static int A = 1;
static {
A = 2;
}
}

public Class Child extends Parent{
public static int B = A;
}

public class Test{
public static void main(String [] args){
System.out.println(Child.B); //
}
}

上面代码会打印2,因为clinit()方法执行顺序,由语句在源文件中的顺序决定的。

IV.clinti()方法,与类的构造函数不同,他不需要显示调用父类构造器,JVM会保证在子类的clinit方法执行前执行。
V.clinit()方法对于类或者接口来说不是必须的。如果一个类没有静态块,也没有赋值操作,就不用clinit()方法。

4.类加载器
(1)
JVM设计团队,把类加载阶段中“通过一个类的全限定名,来获取描述此类的二进制字节流” 这个动作,放到JVM外部,以便让
应用程序自己决定如何去获取所需要的类。实现这个动作的代码块,称为类加载器。
代码:
public static void main(String args []) throws InstantiationException, IllegalAccessException, ClassNotFoundException{
ClassLoader myLoader = new ClassLoader() {
public Class<?> loadClass(String name) throws ClassNotFoundException{
try{
String fileName = name.substring(name.lastIndexOf(".")+ 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if(is == null){
return super.loadClass(name);
}

byte[] b = new byte[is.available()];
return defineClass(name , b , 0 , b.length);
}catch(IOException e){
throw new ClassNotFoundException(name);
}
}
};

Object obj = myLoader.loadClass("com.jvm.ClassLoaderTest").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof ClassLoaderTest);
}

代码构造了一个简单的类加载器,使用这个类加载器加载了一个名为"com.jvm.ClassLoaderTest" 的类,并实例化这个类的对象,
从第一句打印可以看出这个对象确实是com.jvm.ClassLoaderTest 实例化出来的。
但是,从第二句发现,这个对象对com.jvm.ClassLoaderTest 做所属类型检查的时候返回了false,这是因为在jvm中存在了两个 ClassLoaderTest类,
一个是由系统应用程序加载的,一个是由自定义类加载器加载的。
虽然他们来自同一个Class文件,但是,是两个独立的类。
所以,对于任何一个类,都需要由加载他的类加载器,和 这个类本身一同确定其在JVM中的唯一性,每一个类加载器都有一个独立的命名空间。
所以,比较两个类是否相等,只有在这两个类是否由同一个类加载器加载的前提下才有意义,否则,即使这两个类源自同一个Class文件,被同一个JVM加载,
只要加载他们的类加载器不同,那这两个类必定不相等。

(2)双亲委派模型
a.从JVM角度来看,只存在两种类加载器:
I.启动类加载器(Bootstrap ClassLoader) ,使用C++语言实现,是虚拟机自身的一部分。
II.所有其他类加载器,都由JAVA语言实现,独立于虚拟机外部,并且全部继承抽象类java.lang.ClassLoader

b.从开发人员的角度来看,会使用3中系统提供的类加载器

I.启动类加载器(Bootstrap ClassLoader),负责加载 JAVA_HOME\lib 目录中类库,加载到虚拟机内存中。
启动类加载器,无法被java程序直接使用。

II.扩展类加载器(Extension ClassLoader) ,负责加载 JAVA_HOME\lib\ext 目录中的类库

III.应用程序类加载器(Application ClassLoader)又称系统类加载器, 负责加载 javaBuildPath指定的类库,
如果程序中没有指定类加载器,一般情况下,这个就是程序中的类加载器。

VI.自定义类加载器。

c.类加载器关系
启动类加载器(Bootstrap ClassLoader)
^
|
|
扩展类加载器(Extension ClassLoader)
^
|
|
应用程序类加载器(Extension ClassLoader)
^
|
|
自定义类加载器(User ClassLoader)

d.这种层次关系模型,称为类加载器的双亲委派模型。在JDK1.2被引入
双亲委派模型,要求,除了顶层的 启动类加载器 之外,其余的类加载器,都应有自己的父类加载器。这里的父子关系一般不用继承,而是使用组合(Composition)
关系,来复用父类加载器的代码。

e.双亲委派模型,工作过程:
I.如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把请求委派给父类加载器去完成。每一个层次类加载器都是如此。
II.当父类反馈自己无法完成这个加载请求时,子加载器才会尝试自己去尝试加载。

f.好处:JAVA类随着它的类加载器一起具备了,一种带有优先级的层次关系。
例如:java.lang.Object类,存放在rt.jar中,无论哪一个类加载器加载这个类,都是为派给最顶端的Bootstrap ClassLoader加载,因此Object类在程序各种
类加载器环境中都是同一个类。
相反:如果没有双亲委派模型,如果用户自己编写了称为java.lang.Object类,并放在程序的ClassPath中,那么,系统中将会出现多个不同的Object类,Java类型
体系中最基本的行为都无法保证。
综上,双亲委派模型,对于java程序的稳定性非常重要。

jvm--2.类加载机制的更多相关文章

  1. JVM内存结构 JVM的类加载机制

    JVM内存结构: 1.java虚拟机栈:存放的是对象的引用(指针)和局部变量 2.程序计数器:每个线程都有一个程序计数器,跟踪代码运行到哪个位置了 3.堆:对象.数组 4.方法区:字节流(字节码文件) ...

  2. JVM之类加载机制

    JVM之类加载机制 JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 类加载五部分 加载 加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这 ...

  3. JVM的类加载机制全面解析

    什么是类加载机制 JVM把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被JVM直接使用的Java类型,这就是JVM的类加载机制. 如果你对Class文件的结 ...

  4. 大白话谈JVM的类加载机制

    前言 我们很多小伙伴平时都是做JAVA开发的,那么作为一名合格的工程师,你是否有仔细的思考过JVM的运行原理呢. 如果懂得了JVM的运行原理和内存模型,像是一些JVM调优.垃圾回收机制等等的问题我们才 ...

  5. 一文教你读懂JVM的类加载机制

    Java运行程序又被称为WORA(Write Once Run Anywhere,在任何地方运行只需写入一次),意味着我们程序员小哥哥可以在任何一个系统上开发Java程序,但是却可以在所有系统上畅通运 ...

  6. JVM的类加载机制

    虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类加载的过程: 包括加载.链接(含验证.准备 ...

  7. 【JVM】类加载机制

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

  8. 深入理解JVM(3)——类加载机制

    1.类加载时机 类的整个生命周期包括了:加载( Loading ).验证( Verification ).准备( Preparation ).解析( Resolution ).初始化( Initial ...

  9. (转) JVM——Java类加载机制总结

    背景:对java类的加载机制,一直都是模糊的理解,这篇文章看下来清晰易懂. 转载:http://blog.csdn.net/seu_calvin/article/details/52301541 1. ...

  10. JVM虚拟机—JVM的类加载机制

    1 什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构 ...

随机推荐

  1. ubuntu14.04 yuv文件的播放及视频信息的查看

    1.安装ffmpeg sudo add-apt-repository ppa:mc3man/trusty-media sudo apt-get update sudo apt-get install ...

  2. PDF/WORD/EXCEL/PPT 文档在线阅读

    查资料看了2种解决方法: 1.通过办公软件dll转换,用flans去看 2.通过Aspose转换成pdf格式,在用js前台读pdf(我用的pdf.js) 今天我解决的就是WORD/EXCEL/PPT ...

  3. javascript中的原型和继承

    javascript一直是初学者口中的难点,甚至一些有些许工作经验的人也不太明白其中的原理,而我就是那个初学者,前段时间在阮一峰老师的博客上看了一篇文章<javascript继承机制的设计思想& ...

  4. C语言学习 第十一次作业总结

    作业总结 两次的作业,都是和指针有关.从第一次的作业开始,我就多次让同学们思考这个问题:为什么要用指针,为什么在函数的形参中要使用指针.如果能够想明白这2个问题,那么同学们应该会指针的了解就差不多足够 ...

  5. 0103MySQL中的B-tree索引 USINGWHERE和USING INDEX同时出现

    转自博客http://www.amogoo.com/article/4 前提1,为了与时俱进,文中数据库环境为MySQL5.6版本2,为了通用,更为了避免造数据的痛苦,文中所涉及表.数据,均来自于My ...

  6. 反序列化漏洞问题研究之php篇

    php的反序列化反序列化漏洞又称php对象注入(php Object Injection)产生的问题主要分以下两类: 将传来的序列化数据直接unserilize,造成魔幻函数的执行.这种情况在一般的应 ...

  7. Android组件化

    附:Android组件化和插件化开发 App组件化与业务拆分那些事 Android项目架构之业务组件化 Android组件化核心之路由实现 Android组件化开发实践

  8. [展示]手把手教你如何diy门户幻灯片

    第一步后台新建页面:这个就不用说了大家都会  新建后FTP里面会出现如下一个模板页面 第二步从ftp里面下载 template的index.htm文件  给首页模板页面添加JS代码 如下 将这段jS ...

  9. 浅谈servlet

    刚开始接触servlet的时候,其实不是太理解servlet的,后来经过慢慢摸爬滚打式的的学习,有了一点自己的理解. servlet的产生还要从Java和HTTP说起: Java的servletAPI ...

  10. Block 代码快

    1.原理 1.1block类型 a.全局bock块   贯彻整个程序 b.栈块  存在于栈内存中,作用域中使用 c.堆块  自行管理其内存 注*:http://blog.parse.com/learn ...