1.运行时加载优点

提高灵活性,可以在运行时动态加载,连接。例子:面向接口编程,动态绑定实现类(但C++也有动态绑定,说明动态绑定不一定通过运行时加载Class字节码实现,也可能是机器码支持的)

2.编译并在运行时动态加载字节码优点

可在运行时动态获取二进制字节流作为动态代码,比如可以从网络上获取;可以做一个循环,动态从某文件库获取;可以从指定的ClassPath路径获取,比如一个jar包目录

3.与动态类型语言的区别

动态类型语言如JS,可在运行时添加类型属性,方法,在运行时改变类型行为

4.类加载生命周期

加载、验证、准备、解析、初始化、使用、卸载。按这个顺序开始,但交叉执行。验证、准备、解析统称为连接。解析阶段不一定按这个顺序,可以在初始化之后,为了支持上文的动态绑定(运行时绑定)。

5.必须“初始化”(类加载第5阶段)的几种情况(主动引用,有且仅有这些情况时要初始化,初始化钱当然要先“加载”,所以也是必须加载的几种情况)

new实例化对象、读取或设置类static非final字段(final static在编译时存入常量池)、调用类静态方法、类反射调用、初始化一个类时先初始化其父类、虚拟机启动的带main方法的执行主类(虚拟机执行入口)等。

6.不会激发“初始化”的情况(被动引用)

a.引用子类使用其父类的static非final字段(直接定义这个字段的类),只会初始化父类,不会初始化子类。子类是否加载、验证(类加载第1,2阶段),取决于虚拟机具体实现,无规定

b.通过数组定义引用类,不会触发该类初始化。这里触发了一个虚拟机自动生成的代表数组的类的初始化,里面有数组相关属性,比如public的length字段(数组长度)

c.一个类引用另一个类的public static final字段,不会初始化另一个类。该字段引用在编译时进入了前一个类(引用类)的常量池中,编译后就与后一个类完全没关系了。

d.接口初始化:不要求初始化其父类接口,这与类初始化不同。只有使用到父接口比如引用其常量时才会初始化父类接口

7.类加载过程

加载阶段:一个非数组类的加载是开发人员可控性最强的,可以使用系统的引导类加载器,也可使用用户自定义类加载器控制字节流获取方式(下面a所述),即重写loadClass方法。数组类由虚拟机创建,其元素类型由类加载器加载。

a.通过类全限定名获取其字节流,方式:zip/jar/war、网络、运行时计算生成(主要指动态代理生成的"*$Proxy"代理类Class文件)、其他文件生成(如jsp)、数据库读取等

b.字节流静态存储结构转换成方法区运行时数据结构

c.内存中生成代表该类的java.lang.Class对象,作为方法区类数据访问入口

验证阶段:

a.文件格式验证:如魔数开头、主次版本号、常量类型、不符合UTF8的编码

b.元数据验证:语义校验,比如是否有父类、是否继承final类(不允许继承)、是否实现接口、是否覆盖了父类final方法(不允许覆盖)

c.字节码验证:数据流与控制流验证,语义是否合法、符合逻辑。方法体校验:操作数栈数据类型是否与指令码使用类型匹配、指令是否跳到方法体外、方法体内类型转换是否有效

d.符号引用验证:符号引用转为直接引用是在解析阶段进行的,这里是对类自身以外(常量池中各种符号引用)的信息进行匹配校验,比如全限定名是否能找到该类、指定类是否有该符号引用所引用的方法和字段、引用的类、方法、字段访问性(private,protected,public,default)是否可被当前类引用,抛出对应IllegalAccessError、NoSuchMethodError等。

准备阶段:

在方法区将类变量(static)分配内存并设置初始值,通常赋类型零值。如public static int value=123;是赋值为0不是123.赋值123是putstatic指令,在类构造器<clinit>()中,是在初始化阶段才执行的。但public static int value=123;则在准备阶段赋值123.

解析阶段:

将常量池内符号引用替换为直接引用。符号引用:目标不一定已加载入内存。直接引用:直接指向目标的指针、相对偏移量或可间接定位的句柄。引入目标必定在内存中存在。

解析的7类符号引用:类或接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符。

递归加载这些符号引用解析成直接引用后的那些类、父类、接口、父接口,进内存,然后使用直接引用指向它们。

初始化阶段:

执行类构造器<clinit>()方法的过程。

<clinit>()方法由编译器自动搜集所有类变量赋值动作和static{}块合并产生,顺序由源文件中出现顺序决定。static{}块可赋值在其后定义的变量,但不可访问。

public class Test {

  static {

    i = 0;//正常编译

    System.out.println(i);//非法向前引用!!

  }

  static int i = 1;

}

父类<clinit>()方法先于子类<clinit>()执行,由虚拟机调用,而不是子类<clinit>()方法调用。因此父类静态块要优先于子类变量赋值动作。(值为多少的面试题)

static class Parent {

  public static int A = 1;

  static {

    A = 2;

  }

}

static class Sub extents Parent {

  public static int B = A;

}

public static void main(String[] args) {

  System.out.println(Sub.B);

}

B值为2.

接口没有静态块。<clinit>()方法用于变量初始化赋值。不需要先执行父类接口<clinit>()方法,使用时执行。实现接口的类初始化也不需要执行接口<clinit>(),使用时执行。

虚拟机保证<clinit>()方法多线程下安全。如多线程执行,其他线程会阻塞,但退出后其他线程也不会再进入执行。同一个类加载器下(一个类与其加载器一起确定唯一性),一个类型只初始化一次!

JVM类加载机制总结的更多相关文章

  1. JVM基础系列第7讲:JVM 类加载机制

    当 Java 虚拟机将 Java 源码编译为字节码之后,虚拟机便可以将字节码读取进内存,从而进行解析.运行等整个过程,这个过程我们叫:Java 虚拟机的类加载机制.JVM 虚拟机执行 class 字节 ...

  2. JVM总结(四):JVM类加载机制

    这一节我们来总结一下JVM类加载机制.具体目录如下: 类加载的过程 类加载过程概括 说说引用 详解类加载全过程: 加载 验证 准备 解析 初始化 虚拟机把描述类的数据从Class文件加载到内存,并对数 ...

  3. JVM 类加载机制详解

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

  4. Java虚拟机(四):JVM类加载机制

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

  5. JVM类加载机制详解(二)类加载器与双亲委派模型

    在上一篇JVM类加载机制详解(一)JVM类加载过程中说到,类加载机制的第一个阶段加载做的工作有: 1.通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件).而获取的方式,可 ...

  6. JVM类加载机制(转)

    原文出自:http://www.cnblogs.com/ityouknow/p/5603287.html 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运 ...

  7. JVM类加载机制详解

    引言 如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程. 加载 在加载阶段,虚拟机需要完成以下三件事情: 1)通过一个类的全限定名来获取定义此 ...

  8. Android动态加载--JVM 类加载机制

    动态加载,本质上是通过JVM类加载机制将插件模块加载到宿主apk中,并通过android的相关运行机制,实现插件apk的运行.因此熟悉JVM类加载的机制非常重要. 类加载机制:虚拟机把描述类的数据从C ...

  9. Java虚拟机(五):JVM 类加载机制

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

  10. 深入理解JVM虚拟机6:深入理解JVM类加载机制

    深入理解JVM类加载机制 简述:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 下面我们具体 ...

随机推荐

  1. paas容器云

  2. C#基础笔记(第十一天)

    1.复习字符串(1)字符串的不可变性(2)字符串的方法:1)Split() 分割 把字符串中不想要的内容分割掉 返回一个字符串类型的数组 可以添加StringSplitOption.RemoveEmp ...

  3. Inception系列

    从GoogLeNet的Inceptionv1开始,发展了众多inception,如inception v2.v3.v4与Inception-ResNet-V2. 故事还是要从inception v1开 ...

  4. [py]python的继承体系-源码目录结构

    python3安装目录 pip install virtualenv pip install virtualenvwrapper pip install virtualenvwrapper-win m ...

  5. python排序函数sort()与sorted()区别

    sort是容器的函数:sort(cmp=None, key=None, reverse=False) sorted是python的内建函数:sorted(iterable, cmp=None, key ...

  6. CentOS忘记普通用户密码解决办法

    普通用户忘记密码 1.使用root用户登录系统,找到/etc/shadow文件. 2.找到用户名开头的那一行,例如我的用户名为pds,,以冒号为分割符,红色部分是密码加密部分 pds:$1$Civop ...

  7. 找出numpy array数组的最值及其索引

    在list列表中,max(list)可以得到list的最大值,list.index(max(list))可以得到最大值对应的索引 但在numpy中的array没有index方法,取而代之的是where ...

  8. html10

    1.动画fadeIn() - 淡入fadeOut() - 淡出 -通过改变透明度(opacity隐藏之后依旧占着位置)实现节点的显示和隐藏show() - 显示hide() - 隐藏 -同时改变节点的 ...

  9. uva11419 二分图--最小覆盖=最大匹配

    大白书355 // UVa11419 SAM I AM // Rujia Liu #include <cstdio> #include <cstring> #include & ...

  10. linux编程之pipe()函数

    管道是一种把两个进程之间的标准输入和标准输出连接起来的机制,从而提供一种让多个进程间通信的方法,当进程创建管道时,每次 都需要提供两个文件描述符来操作管道.其中一个对管道进行写操作,另一个对管道进行读 ...