JVM学习记录-类加载时机
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是类的加载机制。
在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的。类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:
加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中哦验证、准备、解析3个部分统称为连接(Linking),这7个阶段顺序如下图:

其中加载、验证、准备、初始化、和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始(这里仅仅指的是开始,而不是按部就班的进行或完成,是因为这些阶段通常都是相互交叉的进行的,通常在一个阶段执行的过程中调用、激活另外一个阶段),而解析阶段则不一定,它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定。
那么在什么时候开始类加载过程的第一个阶段(也就是加载)呢?Java虚拟机规范中并没有进行强制约束,这点虚拟机根据自身实现来把握。但对于初始化阶段,虚拟机规范则是严格规定了有且只有5中情况必须立即对类进行初始化(加载,验证,准备肯定要在此之前进行了)。
- 创建类实例的时候,读取或者设置一个类的静态字段(被final修饰,已在编译期把结果放入常量池的除外),以及调用一个类的静态方法的时候。
- 对类进行反射调用的时候,如果没有进行过初始化则需要先出发其初始化过程。
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先出发其父类的初始化过程。
- 当虚拟机启动时,定义了入口(含有main()方法的那个类)的主类,虚拟机会先初始化这个主类。
- 当使用JDK1.7及以上的版本中的动态语言支持时,若一个java.lang.invoke.MethodHandle实例最后的解析结果是:REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先出发它的初始化过程。
虚拟机规范中指出有且只有这5种场景会出发初始化,并且这5种场景的行为称为对一个类的“主动引用”,除此之外所有引用类的方式都不会触发初始化,不触发初始化的也被称为被动引用。
用代码例子来说明被动引用。
/**
* 通过子类引用父类的的静态字段,不会导致子类初始化
*/
public class SuperClass { static {
System.out.println("SuperClass init!");
} public static int value = 888; public static final String JVM_TEST = "JVM TEST";
} /**
* 子类
*/
public class SubClass extends SuperClass { static {
System.out.println("SubClass init!");
} } /**
* 测试
*/
public class Test { public static void main(String[] args){
System.out.println(SubClass.value);
}
}
打印结果为:
SuperClass init!
888
对于静态字段,只有定义这个字段的类才会被初始化,因此通过子类调用其父类中定义的静态字段,只会出发父类的初始化。
/**
* 通过数组定义引用类,不会出发类的初始化
*/
public class Test { public static void main(String[] args){
SuperClass[] supers = new SuperClass[12];
}
}
运行结果并没有打印出“SuperClass init!”,这说明并没有对SuperClass进行初始化,定义数组不会触发类的初始化。
/**
* 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类中,因此不会出发定义常量的类的初始化。
*/
public class Test { public static void main(String[] args){
System.out.println(SuperClass.JVM_TEST);
}
}
运行结果也没有打印出“SuperClass init!”,因为虽然引用了SuperClass的常量,但其实在编译极端通过常量传播优化,已经将此常量存储到了Test类的常量池中,因Test类对此常量的引用,都会转化为Test类对自身常量池的引用了。这说明SuperClass和Test这两个类,在编译阶段完成后就没有任何关系了。
接口的加载过程和类的加载过程步骤上是一致的,但是稍有不同的是上面的例子都是用静态语句块“static{}”来输出初始化信息的,在接口中不能使用“static{}”静态语句块。还有一个不同是:当一个类在初始化的时候,要求其父类全部都已经初始化过了,但是一个接口在初始化的时候,不要求其父接口都初始化过,只有真正使用到父接口的时候(例如:引用父接口中定义的常量)才会初始化。
JVM学习记录-类加载时机的更多相关文章
- JVM学习记录-类加载的过程
类的整个生命周期的7个阶段是:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(Us ...
- JVM学习记录-类加载器
前言 JVM设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外面去实现,以便让应用程序自己决定如何去获取所需要的类.实现这个动作的代码模块称为“类 ...
- JVM学习笔记——类加载和字节码技术篇
JVM学习笔记--类加载和字节码技术篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的类加载和字节码技术部分 我们会分为以下几部分进行介绍: 类文件结构 字节码指令 编译期处理 类 ...
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- JVM学习笔记——类加载过程
JVM学习笔记——类加载过程 类加载模型——双亲委派模型(Parents Delegation Model)也可称为“溯源委派加载模型” Java的类加载器是一个运行时核心基础设施模块,主要是启动之初 ...
- JVM学习记录-线程安全与锁优化(二)
前言 高效并发是程序员们写代码时一直所追求的,HotSpot虚拟机开发团队也为此付出了很多努力,为了在线程之间更高效地共享数据,以及解决竞争问题,HotSpot开发团队做出了各种锁的优化技术常见的有: ...
- JVM学习之类加载
该文使用Hotspot JDK1.7 一.类加载器 1.什么是类加载器 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java ...
- JVM学习记录
本博客是为了自己学习JVM而建立,只记录一些自己学习的经过. 最近在看<深入理解Java虚拟机>这本书,里面的内容,很是乏味,因为看不懂所以就会觉得很枯燥,觉得很枯燥看着看着就犯困,然后就 ...
- jvm学习记录-对象的创建、对象的内存布局、对象的访问定位
简述 今天继续写<深入理解java虚拟机>的对象创建的理解.这次和上次隔的时间有些长,是因为有些东西确实不好理解,就查阅各种资料,然后弄明白了才来做记录. (此文中所阐述的内容都是以Hot ...
随机推荐
- jbpm 工作流(二)
1 概述 本文主要介绍如何将JBPM+Struts+Spring+Hibernate整合在一块.并通过一个简单实例来说明.此实例为一个申请审批的简单流程,并将申请人和审批人记录到数 ...
- mac port 清理
http://popozhu.github.io/2014/10/27/mac-port-%E6%B8%85%E7%90%86/ 使用 mac port 来安装一些软件,时间久了后(也有两年多),更新 ...
- Android 路由框架ARouter最佳实践
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/76165252 本文出自[赵彦军的博客] 一:什么是路由? 说简单点就是映射页面跳转 ...
- ASP.NET MVC中的路由IRouteConstraint方法应用实例
在如下代码的写法中: public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { ro ...
- arcgis api for flex 除去 esri map控件中的logo标志
在程序的applicationComplete方法中添加下列代码 private function reallyHideESRILogo(map : Map) : void { for(var i : ...
- GitHub学习笔记:本地操作
安装过程略,假设你已经注册好了Github, 已经有了一个准备好的程序.我们的一切工作都是基于Git Shell,与GUI客户端无关. 在使用前你先要配置好config中的几个内容,主要是你自己的个人 ...
- JavaScript里面的循环方法小结
一,原生JavaScript中的循环: for 循环代码块一定的次数,它有三个参数,来决定代码块的循环次数,第一个是初始值,第二个是终止值,第三个参数是变化规则: //for循环 for(var i ...
- 使用crypto-js对数据进行AES加密、解密
前段时间做项目有用到数据加密,前端加密,后端解密(前端也可以解密),话不多说进入正题: 第一步: npm i crypto-js -S 第二步: 在需要加密或解密的地方引入crypto-js: imp ...
- linux查看系统的日志------健康检查特性
last https://www.cnblogs.com/anruy/articles/5541675.html Nginx反向代理,健康状态检测,过载保护及配置文件详 ...
- 深入NGINX:nginx高性能的实现原理
深入NGINX:我们如何设计它的性能和扩展性 来源: cnBeta 原文链接 英文原文:Inside NGINX: How We Designed for Performance & Sca ...