中国城市旅游网

>> 您当前的位置: 首页 -> 攻略

Java类加载及对象创建过程详解 资讯推荐

时间:2023-05-05 07:23:19 来源:清一色财经

验证阶段作用是保证Class文件的字节流包含的信息符合JVM规范,不会给JVM造成危害。如果验证失败,就会抛出一个java.lang.VerifyError异常或其子类异常。


(相关资料图)

[[271554]]

类加载过程

类加载的五个过程:加载、验证、准备、解析、初始化。

加载

在加载阶段,虚拟机主要完成三件事:

通过一个类的全限定名来获取定义此类的二进制字节流。将这个字节流所代表的静态存储结构转化为方法区域的运行时数据结构。在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区域数据的访问入口。

验证

验证阶段作用是保证Class文件的字节流包含的信息符合JVM规范,不会给JVM造成危害。如果验证失败,就会抛出一个java.lang.VerifyError异常或其子类异常。验证过程分为四个阶段:

文件格式验证:验证字节流文件是否符合Class文件格式的规范,并且能被当前虚拟机正确的处理。元数据验证:是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言的规范要求字节码验证:主要是进行数据流和控制流的分析,保证被校验类的方法在运行时不会危害虚拟机。符号引用验证:符号引用验证发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生。

准备

准备阶段为变量分配内存并设置类变量的初始化。在这个阶段分配的仅为类的变量(static修饰的变量),而不包括类的实例变量。对已非final的变量,JVM会将其设置成“零值”,而不是其赋值语句的值:pirvate static int size = 12;。那么在这个阶段,size的值为0,而不是12。但final修饰的类变量将会赋值成真实的值。

解析

解析过程是将常量池内的符号引用替换成直接引用。主要包括四种类型引用的解析。类或接口的解析、字段解析、方法解析、接口方法解析。

初始化

在准备阶段,类变量已经经过一次初始化了,在这个阶段,则是通过程序制定的计划去初始化类的变量和其他资源。这些资源有static{}块,构造函数,父类的初始化等。

至于使用和卸载阶段阶段,这里不再过多说明,使用过程就是根据程序定义的行为执行,卸载由GC完成。

双亲委派模型

类加载器按照层次,从顶层到底层,分为以下三种:

启动类加载器(BootstrapClassLoader)这个类加载器负责加载%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。可以通System.getProperty("sun.boot.class.path")查看加载的路径。扩展类加载器(ExtensionClassLoader)负责加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。也可以通过System.out.println(System.getProperty("java.ext.dirs"))查看加载类文件的路径。应用程序类加载器(ApplicationClassLoader)这个加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(Classpath)上所指定的类库,可直接使用这个加载器,如果应用程序没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载。

上图只是类加载的顺序,和类继承无关。ExtClassLoader,AppClassLoder继承URLClassLoader,而URLClassLoader继承ClassLoader。BoopStrap ClassLoder是由C/C++编写的,它本身是虚拟机的一部分,并不是一个java类。

AppClassLoader的父加载器为ExtClassLoader,ExtClassLoader的父加载器为null,BoopStrap ClassLoader为顶级加载器

工作过程

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传递到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

相对应的实现逻辑:先检查类是否被加载过,若没有就调用父加载器的loadClass方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载器加载失败,抛出异常,再调用自己的findClass方法进行加载。

具体示例:

假如我们自定义Test class文件,jvm要加载Test.class的时候:

首先会到自定义加载器中查找,看是否已经加载过,如果已经加载过,则返回字节码。如果自定义加载器没有加载过,则询问上一层加载器(即AppClassLoader)是否已经加载过Test.class。如果没有加载过,则询问上一层加载器(ExtClassLoader)是否已经加载过。如果没有加载过,则继续询问上一层加载(BoopStrap ClassLoader)是否已经加载过。如果BoopStrap ClassLoader依然没有加载过,则到自己指定类加载路径下("sun.boot.class.path")查看是否有Test.class字节码,有则返回,没有通知下一层加载器ExtClassLoader到自己指定的类加载路径下(java.ext.dirs)查看。依次类推,最后到自定义类加载器指定的路径还没有找到Test.class字节码,则抛出异常ClassNotFoundException。

双亲委派的好处

Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类Object,它放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。

判断两个类是否相同是通过classloader.class这种方式进行的,所以哪怕是同一个class文件如果被两个classloader加载,那么他们也是不同的类。

实现自己的加载器,只需要继承ClassLoader,并覆盖findClass方法。

对象创建过程

对象的流程

1. 类加载检查

JVM遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。

如果没有,那必须先执行相应的类的加载过程。

2. 对象分配内存

对象所需内存的大小在类加载完成后便完全确定(对象内存布局),为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。

根据Java堆中是否规整有两种内存的分配方式:(Java堆是否规整由所采用的垃圾收集器是否带有压缩整理功能决定)。

指针碰撞(Bump the pointer)

Java堆中的内存是规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,分配内存也就是把指针向空闲空间那边移动一段与内存大小相等的距离。例如:Serial、ParNew等收集器。

空闲列表(Free List)

Java堆中的内存不是规整的,已使用的内存和空闲的内存相互交错,就没有办法简单的进行指针碰撞了。虚拟机必须维护一张列表,记录哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。例如:CMS这种基于Mark-Sweep算法的收集器。

3. 并发处理

对象创建在虚拟机中时非常频繁的行为,即使是仅仅修改一个指针指向的位置,在并发情况下也并不是线程安全的,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。解决这个问题有两种方案:

同步

虚拟机采用CAS配上失败重试的方式保证更新操作的原子性

本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)

把内存分配的动作按照线程划分为在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存(TLAB)。哪个线程要分配内存,就在哪个线程的TLAB上分配。只有TLAB用完并分配新的TLAB时,才需要同步锁定。

虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。

4. 内存空间初始化

虚拟机将分配到的内存空间都初始化为零值(不包括对象头),如果使用了TLAB,这一工作过程也可以提前至TLAB分配时进行。

内存空间初始化保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

注意:类的成员变量可以不显示地初始化(Java虚拟机都会先自动给它初始化为默认值)。方法中的局部变量如果只负责接收一个表达式的值,可以不初始化,但是参与运算和直接输出等其它情况的局部变量需要初始化。

5. 对象设置

虚拟机对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头之中。

6. 执行init()

在上面的工作都完成之后,从虚拟机的角度看,一个新的对象已经产生了。但是从Java程序的角度看,对象的创建才刚刚开始init()方法还没有执行,所有的字段都还是零。

所以,一般来说(由字节码中是否跟随invokespecial指令所决定),执行new指令之后会接着执行init()方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算产生出来。

对象的内存布局

在HotSpot虚拟机中。对象在内存中存储的布局分为:

对象头实例数据对齐填充

对象头

HotSpot虚拟机的对象头包括两部分信息:运行时数据和类型指针。

运行时数据:用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。类型指针:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。

(并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说,查找对象的元数据并不一定要经过对象本身,可参考对象的访问定位)

HotSpot底层通过markOop实现Mark Word,具体实现位于markOop.hpp文件。markOop中提供了大量方法用于查看当前对象头的状态,以及更新对象头的数据,为synchronized锁的实现提供了基础。[比如说我们知道synchronized锁的是对象而不是代码,而锁的状态保存在对象头中,进而实现锁住对象]。

有关synchronized的进一步介绍,可以点击查看:详解Java多线程锁之synchronized

实例数据

实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类中继承下来的,还是在子类中定义的,都需要记录下来。HotSpot虚拟机默认的分配策略为longs/doubles、ints、shorts/chars、bytes/booleans、oop,从分配策略中可以看出,相同宽度的字段总是分配到一起。

对齐填充

HotSpot虚拟机要求对象的起始地址必须是8字节的整数倍,也就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐的时候,就需要通过对齐填充来补全。

对象的访问定位

java程序需要通过引用(ref)数据来操作堆上面的对象,那么如何通过引用定位、访问到对象的具体位置。

对象的访问方式由虚拟机决定,java虚拟机提供两种主流的方式

1.句柄访问对象

2.直接指针访问对象。(Sun HotSpot使用这种方式)

句柄访问

简单来说就是java堆划出一块内存作为句柄池,引用中存储对象的句柄地址,句柄中包含对象实例数据、类型数据的地址信息。

优点:引用中存储的是稳定的句柄地址,在对象被移动【垃圾收集时移动对象是常态】只需改变句柄中实例数据的指针,不需要改动引用【ref】本身。

直接指针

在这种方式中,JVM栈中的栈帧中的本地变量表中所存储的引用地址就是实例数据的地址。通过这个引用就能直接获取到实例数据的地址。

其实引用所指向的对内存中的对象数据有两部分组成,一部分就是这个对象实例本身,另一部分是对象类型在方法区中的地址。

优点:优势很明显,就是速度快,相比于句柄访问少了一次指针定位的开销时间。由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。虚拟机Sun HotSpot而言,它是使用第二种方式进行对象访问的,但从整个软件开发的范围来看,各种语言和框架使用句柄来访问的情况也十分常见。

关键词:
5月31日基金净值:创金合信工业周期股票A最新净值2.2615,涨0.16%

5月31日,创金合信工业周期股票A最新单位净值为2 2615元,累计净值为2 2615元,较前一交易日上涨0 16%。历

拒绝德国召唤的中超旧将,22岁还是拜仁鸡肋,27岁却成德甲助攻王

德国队这种历史悠久、战绩辉煌的老牌豪门,对于职业球员的吸引力,那是毋庸置疑的。那些同样也有资格代表其

大降价!你爱吃的它,每斤价格已跌破 10 元! 环球快看

最近,在江苏连云港,随着小龙虾上市量大幅上升,小龙虾的价格也是一跌再跌,最低甚至跌破10元一斤。在连云

白色恋人歌词什么意思_白色恋人完整歌词

1、白人情侣展示小精灵玩耍的花园。在北欧神话中,精灵通常闪亮、美丽、温顺、开朗、热情,能与花、树、鸟

利率期货押注美联储6月加息概率降至三分之一 当前时讯

利率期货押注美联储6月加息概率降至三分之一:美国联邦基金利率期货目前押注美联储6月加息的可能性为三分之

国家统计局:5月份房地产等行业商务活动指数均位于收缩区间

建筑业商务活动指数为58 2%,低于上月5 7个百分点,仍位于较高景气区间,建筑业继续保持较快增长。从市场预

世界新资讯:谷歌街景地图游戏_谷歌街景地图

1、看看这个案例,楼猪应该就会用了。2、1 打开GoogleMap。3、2 搜寻47 110579,9 22

新消息丨声卡是什么设备_声卡是什么

想必现在有很多小伙伴对于声卡是什么方面的知识都比较想要了解,那么今天小好小编就为大家收集了一些关于声

世界头条:黎都农商行工会“三步走”推动业务发展

黄河新闻网长治讯:长治黎都农商银行工会紧紧围绕业务发展工作大局,依托工会工作实际,充分发挥工会维护、

上海电影节电子票可以入场吗2023 全球今头条

上海电影节电子票可以入场吗?答:不可以。2023上海国际电影节所有场次均需持有效纸质票入场。购票后,您可

沪镍跌近5%,逻辑有变?_天天信息

5月31日,镍价大幅走低,一方面市场悲观情绪压制,另一方面有消息称,印尼将暂停征收镍产品出口税,这将维

全球快看:聚焦企业切实需求,松江九里亭将法治宣传和政策宣讲“一站送达”

日前,松江区九里亭街道在九里工坊人才服务站组织开展“百场讲座进企业”法治宣传活动和松江区创业扶持...

航天员升空越来越舒适的背后是他们无数次的钻研和试验

30日,神舟十六号载人飞船发射取得圆满成功,景海鹏、朱杨柱、桂海潮3名航天员入驻“天宫”,与神舟十五...

戛纳卡尔顿丽晶酒店于戛纳电影节绮丽新生 群星齐聚闪耀蔚蓝海岸-全球观热点

2023年5月31日(法国戛纳)——日前,洲际酒店集团携手第76届戛纳电影节,共庆全球重量级旗舰地标戛纳卡...

席梦思需要垫被子吗(席梦思上面还需要垫什么)

相信大家对席梦思需要垫被子吗,席梦思上面还需要垫什么的问题都很疑惑,这个问题很多人还不知道怎么解决,

当前观点:Framework 全新 16 英寸模块化笔记本采用京东方半定制屏幕:2.5K 165Hz

本文转自:IT之家作者:孤城模块化笔记本厂商Framework将在今年晚些时候推出16英寸的Laptop16大屏型号,显

最新创意小户型设计:实用·简约_对于最新创意小户型设计:实用·简约简单介绍

1、《最新创意小户型设计:实用·简约》针对小户型业主的装修特点量身订制,精选三百余个最新简约实用的...

当前快看:第八届中国民航航空油料员(化验员)职业技能大赛开启

原标题:第八届中国民航航空油料员(化验员)职业技能大赛开启工人日报-中工网记者蒋菡5月30日,第八届中国

【世界聚看点】ETF观察丨港股两大指数跌超2%,百济神州跌超5%,恒生医疗ETF(513060)年初至今份额增超68亿份

今日(5月31日),港股两大指数早盘走低,截至午间收盘,恒指跌2 25%,恒生科技指数跌2 47%。12个恒生综合

杨延昭被穆桂英打是哪集_杨延昭_环球焦点

1、委以重任杨延昭幼时沉默寡言,儿童时喜欢多做军阵之类的游戏。2、杨业曾经说:“这个儿子像我。3、”...

总局复函!食品旧版包装材料可以延期使用6个月_天天聚看点

提醒近期微信公众号平台调整了推送机制,点击上方 “食药法苑 ”  点击右上角“ ”  点选“...

2023吉祥文化金银币价格表(2023年05月31日) 每日焦点

金投网提供2023吉祥文化金银币价格表(2023年05月31日)相关信息!

x 广告
x 广告