简介

在java中,类的声明周期总共分为以下几种: 加载(Loading),验证(Verification),准备(Preparation),解析(Analysis),

初始化(Initialization),使用(Using),卸载(Unloading)。其中,验证,准备,解析统称为连接(Linking)如图

一、加载:

在加载阶段,JVM需要完成以下准备:

通过一个类的全限定名来获取定义此类的二进制字节流(并非要从class文件获取,也可从jar或war中读取,也可以在运行时动态生成,还可以编译jsp时获取)

二、验证:

验证是为了确保class文件中的字节流包含的信息符合JVM的要求,并且不会危害JVM自身的安全,验证大致分为四中方法:

  1. 文件格式验证: 验证字节流是否符合class文件的规范,例:主次版本号是否在当前JVM范围内,常量池中的常量是否有不被支持的类型
  2. 元数据验证: 对字节码描述的信息进行语义分析(javac编译阶段的语义分析),以保证其描述信息符合java语言规范要求
  3. 字节码验证: 通过数据流和控制流分析,确保程序是合法的,符合逻辑的
  4. 符号引用验证: 确保解析动作能正确执行

PS: 验证阶段是很重要的,但不是必须的,如果所引用的类已经经过了反复校验,可以使用 -Xverifynone参数来关闭一些验证措施,

用来缩短JVM加载时间

三、准备:

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。

这里进行的内存分配仅包含类变量(被static修饰的变量),不包含实例变量(区别见末尾)。

初始值例: public static int value = 123;

如上声明的话value的值会在准备阶段后为0而不是123。因为此时尚未执行任何java方法,value被赋值123是程序被编译后存放于

类构造器<client>中。但是还有一种特殊情况:

初始值例:public static final int value = 123;

这时在准备阶段后会为value生成ConstantValue属性,赋值为123而非0。

类变量(静态变量):

  1. 在类中被static修饰,并且必须在构造方法和语句块之外
  2. 无论一个类创建了多少变量,类只拥有类变量的一份拷贝
  3. 类变量在程序开始是创建,程序结束时销毁
  4. 静态变量存储在静态存储区,经常被声明为常量
  5. 静态变量可以通过className,VariableName访问到

实例变量:

  1. 声明在类中,不在方法,构造方法,语句块之内
  2. 当一个对象被实例化之后,每个实例变量的值就跟着确定
  3. 实例变量在对象创建是创建,对象销毁时销毁
  4. 实例变量的值应至少被一个方法,构造方法或语句块引用,使得外部可以用这些方法获取实例变量的值
  5. 实例变量可以直接通过变量名访问,但在静态方法和其它类中,应使用完全限定名:ObjectReference.VariableName

四、解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。主要针对类或接口,字段,类方法。

接口方法:   接口方法,方法类型,方法句柄和调用点类型。

符号引用:   符号引用与虚拟机实现的布局无关,引用的目标不一定已经加载到内存中。各种虚拟机实现的内容布局可以

各不相同,但它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在java虚拟机规范的class文件中。

直接引用:   直接引用可以是指向目标的指针,相对偏移量或一个能间接定位到目标的句柄,如果有了直接引用,那引用的

目标必定已在内存中存在。

五、初始化

初始化是类加载的最后一个阶段,前面加载阶段除了加载阶段可以自定义加载器以外,其他都由JVM主导,初始化阶段才是真正

执行类中定义的java代码。

初始化阶段是执行类构造器<clinit>()方法过程。<clinit>()方法是有编译器自动收集类中所有类变量的赋值动作和静态语句块static{}

中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序来决定的,静态语句块只能访问到定义在静态语句块之前

的变量,定义在它之后的变量在前面的静态语句可以赋值,但不能访问。

例:

static {

i = 0;

System.out.println(i);

// Error:Cannot reference a field before it is (非法向前应用)

}

static int i = 1;

虚拟机会保证子类的<clinit>()执行前,父类的<clinit>()已执行完毕,<clinit>()方法对于类或是接口来说不是必须的,如果一个类中没有静态语句块,

也没有对变量的赋值操作,那么编译器可以不为这个类产生<clinit>()方法。

接口中不能使用静态语句块,但仍有变量的初始化赋值操作,因此接口也会生成<clinit>()方法而不需要先执行父类的<clinit>()方法,只有当父类接口

中定义的变量使用时,父接口才初始化,还有,接口的实现类在初始化时也不会执行接口的<clinit>()方法。

虚拟机会保证一个类的<clinit>()方法在多线程的环境中被正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个线程会执行<clinit>()

方法,其余的线程都需要阻塞等待。如果类中<clinit>()方法有耗时很长的操作,就可能会造成多个线程阻塞,在实际应用中这种阻塞往往是隐藏的。

PS:  其它线程虽然被阻塞了,但是如果执行<clinit>()方法的线程退出方法,其它线程也不会再次进入<clinit>()方法。同一个类加载器下,一个类

只会被初始化一次。

*: 虚拟机严格规范了只有五中情况下必须对类进行初始化操作(jdk1.7,加载,验证,准备,解析需要在这之前开始)

  1. 遇到new,getStatic,pubStatic,invokeStatic这四条字节码指令时,没有初始化的类要进行初始化
  2. 使用java,lang,reflect包的方法对类进行反射调用的时候,没有初始化的类要进行初始化
  3. 初始化一个类时,如果父类没有初始化,则要先初始化父类
  4. 虚拟机启动时,用户需要指定一个主类(main函数的类),虚拟机会先初始化主类
  5. 当使用jdk1.7动态支持时,如果java.lang,invoke.MethodHandle实例最后的解析结果REF_getStatic, REF_pubStatic,REF_invokeStatic的方法句柄时,没有初始化的类要进行初始化

* : 不会触发初始化的几种情况:

  1. 通过子类引用父类静态字段,只会触发父类初始化,不会触发子类
  2. 定义对象数组,不会触发初始化
  3. 常量在编译期间会存入调用类常量池中,本质上没有直接引用定义常量的类,不会触发初始化
  4. 通过类名获取的class对象,不会触发初始化
  5. 通过class.forName加载指定类时,若指定参数initialize为false,不会初始化。这个参数就是告诉虚拟机是否执行初始化命令
  6. 通过classLoader默认的LoadClass方法,不会触发初始化

JVM系列-类加载机制的更多相关文章

  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——Java类加载机制总结

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

  7. JVM的类加载机制

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

  8. 【JVM】类加载机制

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

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

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

随机推荐

  1. Tomcat处理HTTP请求原理

    一.Tomcat是什么? Tomcat是一个Web应用服务器,同时也是一个Servlet/JSP容器.Tomcat作为Servlet容器,负责处理客户端请求,把请求传送给Servlet,并将Servl ...

  2. layer弹层content写错导致div复制了一次,导致id失效 $().val() 获取不到dispaly:none div里表单的值

    ​ 错误之源: $("a.consult").click(function () {         lib_consult_html = $('#consult-html').h ...

  3. xtu summer individual 6 F - Water Tree

    Water Tree Time Limit: 4000ms Memory Limit: 262144KB This problem will be judged on CodeForces. Orig ...

  4. hdu 4770 状压+枚举

    /* 长记性了,以后对大数组初始化要注意了!140ms 原来是对vis数组进行每次初始化,每次初始化要200*200的复杂度 一直超时,发现没必要这样,直接标记点就行了,只需要一个15的数组用来标记, ...

  5. JS基础:函数

    函数声明和函数表达式 在 JS 中定义函数的方式有两种:一种是函数声明,一种是函数表达式. 例如: //函数声明 function fun() { ... } //函数表达式 var f = func ...

  6. 【git】Git 提示fatal: remote origin already exists 错误解决办法

    今天使用git 添加远程github仓库的时候提示错误:fatal: remote origin already exists. 最后找到解决办法如下: 1.先删除远程 Git 仓库 $ git re ...

  7. hdu - 1072 Nightmare(bfs)

    http://acm.hdu.edu.cn/showproblem.php?pid=1072 遇到Bomb-Reset-Equipment的时候除了时间恢复之外,必须把这个点做标记不能再走,不然可能造 ...

  8. Codeforces Round #414

    A =w= B qvq C(贪心) 题意: Alice和Bob分别有长度为n(n<=1e5)的字符串,Alice先手,每次从自己的字符串中抽出一个字母放到新字符串的某个位置,一共轮流n次,也就是 ...

  9. Chains (链 )

    Indy 中的工作队列系统,调度器,和链引擎都被叫做链. 当使用链的时候,一个基于链的 IOHandler 存储工作项目到有关的工作队列中.在一个工作单元被完成以前,执行这个工作单元的纤程是无法做其它 ...

  10. GNS3配置SecureCRT

    C:\SecureCRT\SecureCRT.exe /script D:\GNS3\DyRouter.vbs /T /telnet 127.0.0.1 %p "D:\Program Fil ...