JVM-ClassLoader(转)

在加载阶段主要用到的是方法区:
方法区是可供各条线程共享的运行时内存区域。存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方法。
如果把方法的代码看作它的“静态”部分,而把一次方法调用需要记录的临时数据看做它的“动态”部分,那么每个方法的代码是只有一份的,存储于JVM的方法区中;每次某方法被调用,则在该调用所在的线程的的Java栈上新分配一个栈帧,用于存放临时数据,在方法返回时栈帧自动撤销。
在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成验证、准备、解析(可选的)。
装载:查找和导入类或接口的二进制数据;
链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
验证:检查导入类或接口的二进制数据的正确性;
准备:给类分配所需存储空间;
解析:将常量池中符号引用转成直接引用,根据实际需要,可以推迟到初始化之后进行;
初始化:也就是为变量赋予正确的初始值,如激活类的静态变量,初始化Java代码和静态Java代码块
2.1 装载
装载阶段主要是将java字节码以二进制的方式读入到jvm内存中,然后将二进制数据流按照字节码规范解析成jvm内部的运行时数据结构。java只对字节码进行了规范,并没有对内部运行时数据结构进行规定,不同的jvm实现可以采用不同的数据结构,这些运行时数据结构是保存在jvm的方法区中(hotspot jvm的内部数据结构定义可以参见撒迦的博文借助HotSpot SA来一窥PermGen上的对象)。当一个类的二进制解析完毕后,jvm最终会在堆上生成一个java.lang.Class类型的实例对象,通过这个对象可以访问到该类在方法区的内容。
jvm规范并没有规定从二进制字节码数据应该如何产生,事实上,jvm为了支持二进制字节码数据来源的可扩展性,它提供了一个回调接口将通过一个类的全限定名来获取描述此类的二进制字节码的动作开放到jvm的外部实现,这就是我们后面要讲到的类加载器,如果有需要,我们完全可以自定义一些类加载器,达到一些特殊应用场景。由于有了jvm的支持,二进制流的产生的方式可以是:
(1) 从本地文件系统中读取
(2) 从网络上加载(典型应用:java Applet)
(3) 从jar,zip,war等压缩文件中加载
(4) 通过动态将java源文件动态编译产生(jsp的动态编译)
(5) 通过程序直接生成。
2.2 连接
连接阶段主要是做一些加载完成之后的验证工作,和初始化之前的准备一些工作,它细分为三个阶段。
2.2.1 验证
验证是连接阶段的第一步,它主要是用于保证加载的字节码符合java语言的规范,并且不会给虚拟机带来危害。比如验证这个类是不是符合字节码的格式、变量与方法是不是有重复、数据类型是不是有效、继承与实现是否合乎标准等等。按照验证的内容不同又可以细分为4个阶段:文件格式验证(这一步会与装载阶段交叉进行),元数据验证,字节码验证,符号引用验证(这个阶段的验证往往会与解析阶段交叉进行)。
2.2.2 准备
准备阶段主要是为类的静态变量分配内存,并设置jvm默认的初始值。对于非静态的变量,则不会为它们分配内存。
在jvm中各类型的初始值如下:
int,byte,char,long,float,double 默认初始值为0
boolean 为false(在jvm内部用int表示boolean,因此初始值为0)
reference类型为null
对于final static基本类型或者String类型,则直接采用常量值(这实际上是在编译阶段就已经处理好了)。
2.2.3 解析
解析过程就是查找类的常量池中的类,字段,方法,接口的符号引用,将他们替换成直接引用的过程。
a.解析过程主要针对于常量池中的CONSTANT_Class_info,CONSTANT_Fieldref_info,CONSTANT_Methodref_info及CONSTANT_InterfaceMethodref_info四种常量。
b. jvm规范并没有规定解析阶段发生的时间,只是规定了在执行anewarray,checkcast,getfield,getstatic,instanceof,invokeinterface,invokespecial,invokespecial,invokestatic,invokevirtual,multinewaary,new,putfield,putstatic这13个指令应用于符号指令时,先对它们进行解析,获取它们的直接引用.
c. jvm对于每个加载的类都会有在内部创建一个运行时常量池(参考上面图示),在解析之前是以字符串的方式将符号引用保存在运行时常量池中,在程序运行过程中当需要使用某个符号引用时,就会促发解析的过程,解析过程就是通过符号引用查找对应的类实体,然后用直接引用替换符号引用。由于符号引用已经被替换成直接引用,因此后面再次访问时,无需再次解析,直接返回直接引用。
2.3 初始化
初始化阶段是根据用户程序中的初始化语句为类的静态变量赋予正确的初始值。这里初始化执行逻辑最终会体现在类构造器方法<clinit>()方中。该方法由编译器在编译阶段生成,它封装了两部分内容:静态变量的初始化语句和静态语句块。
2.3.1 初始化执行时机
jvm规范明确规定了初始化执行条件,只要满足以下四个条件之一,就会执行初始化工作
(1) 通过new关键字实例化对象、读取或设置类的静态变量、调用类的静态方法(对应new,getstatic,putstatic,invokespecial这四条字节码指令)。
(2) 通过反射方式执行以上行为时。
(3) 初始化子类的时候,会触发父类的初始化。
(4) 作为程序入口直接运行时的主类。
2.3.2 初始化过程
初始化过程包括两步:
(1) 如果类存在直接父类,并且父类没有被初始化则对直接父类进行初始化。
(2) 如果类当前存在<clinit>()方法,则执行<clinit>()方法。
需要注意的是接口(interface)的初始化并不要求先初始化它的父接口。(接口不能有static块)
2.3.3 <clinit>()方法存在的条件
并不是每个类都有<clinit>()方法,如下情况下不会有<clinit>()方法:
a. 类没有静态变量也没有静态语句块
b.类中虽然定义了静态变量,但是没有给出明确的初始化语句。
c.如果类中仅包含了final static 的静态变量的初始化语句,而且初始化语句采用编译时常量表达时,也不会有<clinit>()方法。
2.3.4 并发性
在同一个类加载器域下,每个类只会被初始化一次,当多个线程都需要初始化同一个类,这时只允许一个线程执行初始化工作,其他线程则等待。当初始化执行完后,该线程会通知其他等待的线程。
2.4 在使用过程中类,对象在方法区和堆上的分布状态
public class TestThread extends Thread implements Cloneable {
public static void main(String[] args) {
TestThread t = new TestThread();
t.start();
}
}
上面这代码中TestThread及相关类在jvm运行的存储和引用情况如下图所示:
t在main线程VM Stack的局部变量表中
其中 t 作为TestThread对象的一个引用存储在线程的栈帧空间中,Thread对象及类型数据对应的Class对象实例都存储在堆上,类型数据存储在方法区,前面讲到了,TestThread的类型数据中的符号引用在解析过程中会被替换成直接引用,因此TestThread类型数据中会直接引用到它的父类Thread及它实现的接口Cloneable的类型数据。
在同一个类加载器空间中,对于全限定名相同的类,只会存在唯一的一份类的实例及类型数据。实际上类的实例数据和其对应的Class对象是相互引用的。

http://my.oschina.net/tryUcatchUfinallyU/blog/144133
JVM-ClassLoader(转)的更多相关文章
- jvm classLoader architecture :
jvm classLoader architecture : a.Bootstrap ClassLoader/启动类加载器 主要负责jdk_home/lib目录下的核心 api 或 - ...
- JVM ClassLoader加载过程
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是Java虚拟机的类加载机制. 1)三个类加载器: bootstra ...
- 深入浅出 JVM ClassLoader
# 前言 在 JVM 综述里面,我们说,JVM 做了三件事情,Java 程序的内存管理, Java Class 二进制字节流的加载(ClassLoader),Java 程序的执行(执行引擎).我们也说 ...
- JVM 系列 ClassLoader
JVM 系列()ClassLoader 在前面一节中,主要介绍了 Class 的装载过程,Class 的装载大体上可以分为加载类.连接类和初始化 3 个阶段.本小节将主要介绍绍 Java 语言中的 C ...
- 别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】
目录 1.什么是类的加载(类初始化) 2.类的生命周期 3.接口的加载过程 4.解开开篇的面试题 5.理解首次主动使用 6.类加载器 7.关于命名空间 8.JVM类加载机制 9.双亲委派模型 10.C ...
- [Tomcat] Tomcat的classloader
定义 同其他服务器应用一样,tomcat安装了各种classloader(classes that implement java.lang.ClassLoader) Bootstrap | Syste ...
- 类加载器ClassLoader之jar包隔离
小引子 最近做了一个根据同一模块的不同jar版本做同时测试的工具,感觉挺有意思,特此记录. 类加载器(ClassLoader)是啥? 把类加载阶段中的"通过一个类的全限定名(博主注:绝对路径 ...
- 深入理解ClassLoader(五)—类的卸载
原文地址:http://yhjhappy234.blog.163.com/blog/static/316328322011101413730764/?suggestedreading&wumi ...
- 深入剖析Classloader(二)--根类加载器,扩展类加载器与系统类加载器
原文地址:http://yhjhappy234.blog.163.com/blog/static/31632832201152555245584/?suggestedreading&wumii ...
- 深入剖析Classloader(一)--类的主动使用与被动使用
原文地址:http://yhjhappy234.blog.163.com/blog/static/3163283220115573911607 我们知道java运行的是这样的,首先java编译器将我们 ...
随机推荐
- 循序渐进DB2(第2版)——DBA系统管理、运维与应用案例
<循序渐进DB2(第2版)——DBA系统管理.运维与应用案例> 基本信息 作者: 牛新庄 出版社:清华大学出版社 ISBN:9787302323013 上架时间:2013-7-3 出 ...
- Unix C++(boost) 线程同步和线程组
#include <boost/thread.hpp> #include <iostream> #include <vector> #include <cst ...
- android listview综合使用演示样例_结合数据库操作和listitem单击长按等事件处理
本演示样例说明: 1.自己定义listview条目样式,自己定义listview显示列数的多少,灵活与数据库中字段绑定. 2.实现对DB的增删改查,而且操作后listview自己主动刷新. 3.响应用 ...
- 全球最流行的66款App的共同规律
根据苹果AppStore.Google Play.App Annie.亚马逊 AppStore及Windows Phone 应用商店历年的公开数据统计,以下66个非游戏类应用正在全球范围内流行,持续时 ...
- Unix系统解压tar包时出现@LongLink错误
Unix系统上使用tar命令解压tar包后,多了一个@LongLink的文件,并且原来的tar包解压后不完整.网上查了下,原因是AIX系统上tar命令自身的一个缺陷.解决办法:把该tar包上传到lin ...
- SQL 去除小数点后无效 0 的方法
select convert(float,10.0000) 就是这么简单
- 404 Not Find When using Owin with OAuth
在使用Owin+OAuth时,一步一步的测试时,在用 OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = ...
- 各版本IIS安装方法
各版本IIS安装方法 Windows 2000 V5.0 将操作系统安装光盘放入光驱,打开“控制面板”→“添加或删除程序”→“添加/删除 Windows 组件”,勾选“Internet信息服务(I ...
- PHP 执行系统外部命令 system() exec() passthru()
区别: system() 输出并返回最后一行shell结果. exec() 不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面. passthru() 只调用命令,把命令的运 ...
- 关于EditText组件在android4.4W中出现黄色感叹号的问题?
今天用eclipse编写android4.4W的项目,在XML中用到了EditText组件来写文本框,结果出现了黄色感叹号,还出现一句 This text field does not specify ...