博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM 类加载机制、对象的创建过程
阅读量:2421 次
发布时间:2019-05-10

本文共 3525 字,大约阅读时间需要 11 分钟。

目录

 

类加载:jvm读取.class文件,解析获取类的信息,生成对应的Class对象。

 

类加载的过程

主要分为加载、连接、初始化3个阶段,连接又可细分为验证、准备、解析3个阶段。

 

1、加载 Loading

  • 通过类的全限定名获取类的二进制字节流
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  • 在内存中生成对应的Class对象
     

2、连接 Linking

加载是获得Class对象,连接是把Class对象放到java运行时环境中,即把Class对象连接到jre。

(1)验证

  • 校验被加载的类是否符合jvm要求,内部结构是否正确,和其他类是否协调一致,保证类的正确性
  • 主要包括文件格式验证、元数据验证、字节码验证、符号引用验证
     

(2)准备

  • 给类的静态成员在方法区中分配内存空间并设置初始值,如果静态成员还使用了final修饰,会在此阶段显式赋指定的值。
  • 静态成员是在方法区中分配内存,实例成员是在堆中分配内存
     

(3)解析

  • 将Class对象中的符号引用替换为直接引用
  • 符号引用是用符号表示引用,直接引用是直接指向目标的指针、相对偏移量、句柄等。
  • Class对象是第一步加载时生成的,那时还没有分配内存,不知道该引用对应的内存地址,只能用符号引用暂时表示内存地址。连接的准备阶段已经分配了内存,确定了引用的内存地址,在解析阶段可以用直接引用(内存地址)替换掉符号引用。

 

3、初始化 Initialization

  • 执行初始化方法 <clinit> (),对类的静态成员进行初始化
  • 静态代码块在此阶段执行,静态成员变量在此阶段被显式赋值
  • <clinit> ()是java编译器提取类中的所有静态成员变量的赋值动作和静态代码块中的语句合并而来,类中存在静态成员变量、静态代码块时才会生成<clinit> 方法,否则不会有。

 

类加载器

类加载器用于完成类的加载,一共4种,jvm提供了前3种,按加载的先后顺序依次为

  • Bootstrap ClassLoader :根类加载器,又叫做引导类加载器,负责加载jdk中的核心类库。根类加载器是用C++写的,不继承java.lang.ClassLoader。
  • Extension ClassLoader :扩展类加载器,负责加载jdk中的扩展类库(非核心类库),扩展类加载器是用java写的,继承自ClassLoader。
  • System ClassLoader:系统类加载器,负责加载第三方jar包、我们自己写的类。系统类加载器是用java写的,继承自ClassLoader。
  • 自定义的类加载器:可以继承ClassLoader类来实现自定义的类加载器
     

jvm如何判断2个Class对象是否是同一个类的

  • 全限定类名是否相同
  • 使用的类加载器是否相同

 

类加载机制

jvm提供了3种类加载机制,这3种机制共同作用,一起完成类的加载

  • 全盘负责:使用类加载器加载一个类时,这个类中所引用的类也由该类加载器进行加载
  • 父类委托:也叫作双亲委派机制,如果要加载的类存在父类,先使用父类的类加载器来加载子类,如果父类的类加载器加载不了,再使用类本身的类加载器进行加载。父类委托可以避免类的重复加载。
  • 缓存机制:jvm会缓存加载过的类的class对象,要使用某个类时,先在缓存中搜索是否有对应的class对象,有就直接使用、不再加载,没有才加载。运行程序后,如果修改了某个类,需要重启jvm才会生效,不然使用的是之前缓存的class对象。

 

类加载器的使用流程如下

在这里插入图片描述

  • 类加载是按需加载,使用该类时才加载
  • 因为存在缓存机制,一个类只加载一次,且一个类在内存中最多只有一个class对象。类加载生成的class对象用全类名唯一标识。
    -使用双亲委派机制去加载类:避免重复加载同一个.class文件

 

类加载时机

虚拟机规范定义了只有在以下5种情况下,才会进行类加载

1、虚拟机启动 执行应用程序 加载主类

2、加载子类时,作为父类被先加载

3、对类进行反射调用,比如Class.forname(“Xxx”)、newInstance()

4、MethodHandle和VarHandle可以看作轻量级的反射调用机制,使用这2个调用时需要先使用findStaticVarHandle初始化要调用的类

5、遇到 new 、 getstatic、putstatic、invokestatic 这4条直接码指令之一

  • new:创建对象
  • getstatic:访问类的静态的成员变量
  • putstatic:给类的静态成员变量赋值
  • invokestatic:调用类的静态方法

 

类加载方式

类加载方式有两种

  • 隐式加载:使用new创建对象,隐式调用类加载器,加载对应的类到 JVM 中,这是最常见的类加载方式
  • 显式加载:使用 loadClass()、forName() 等方法显式加载需要的类,获取到 Class 对象后,调用 Class 对象的 newInstance() 方法来创建类的实例
     

两种类加载方式的区别

  • 隐式加载能直接获取类的实例,显式加载需要调用 Class 对象的 newInstance() 方法来生成类的实例
  • 隐式加载能使用带参的构造函数,而Class对象的 newInstance() 不能传入参数,如果要使用带参的构造函数,可以通过反射获取到该类带参的构造方法,通过反射调用带参的构造方法来创建实例
     

loadClass() 、 forName() 的区别

loadClass() 只执行类加载的第一步:加载,后续操作均未进行;Class.forName() 执行了类加载的整个过程(3步)。

 

对象的创建过程

1、检测是否已加载对应的类,如果没加载则先加载对应的类。

 

2、计算对象占用的空间大小,在堆中分配内存空间。

引用类型的成员变量,只分配引用的空间即可。

 

分配内存的2种方式

  • 指针碰撞:适用于内存规整、连续的情况,所有已使用的内存在一边,空闲的内存在另一边,以一个指针标识分界点,先在空闲的一边开辟一块新内存,再移动指针。
  • 空闲列表:适用于已使用的内存、空闲内存相互交错,内存琐碎、不规整的情况,jvm维护一个空闲列表,记录空闲的内存块,先从空闲列表中找一块合适的内存块进行分配,再更新空闲列表。

堆内存是否规整由使用的垃圾收集器是否带有压缩整理功能决定,jvm会根据堆内存是否规整选择合适的方式进行内存分配。

这2种内存分配方式都具有多个(2个)步骤,不是原子性操作,对象创建是非常频繁的行为,需要解决多线程创建对象时的内存分配的并发问题。

 

解决内存分配并发问题的2种方式

  • CAS算法实现乐观锁,搭配失败重试来保证内存分配的成功率
  • 本地线程分配缓冲:Thread Local Allocation Buffer,TLAB,在堆中预先给每个线程分配一小块内存作为分配缓冲区,每条线程在各自的分配缓冲区进行分配,分配缓冲区用完了才使用CAS方式。

jvm是否使用TLAB方式,可以通过jvm参数指定

#+是使用,-是不使用-XX: +UseTLAB

 

3、初始化分配的内存空间。

是按实例成员变量的类型进行计算、分配内存空间的,此时初始化实例成员变量为默认值。静态成员在类加载时就已初始化。

 

4、设置对象头的相关信息

eg. GC分代年龄、对象的hashCode、锁状态标识、元数据信息

 

5、执行init()方法对对象进行初始化。

  • init()方法由编译器在编译时提取普通初始化块、构造方法、实例成员的赋值操作代码组成,在创建对象时用于显式初始对象的实例成员变量。
  • <clinit> ()由编译器在编译时提取静态初始化块、静态成员变量的赋值操作代码构成,在类加载时用于显式初始化类的静态成员变量。

 

对象的内存布局

在这里插入图片描述

1、对象头用于存储对象的元数据信息

  • Mark Word 部分存储对象自身的运行时数据,比如哈希值、gc分代年龄、锁状态标识
  • 类型指针指向对象所属的类的元数据,标识对象所属的类

2、实例数据存储的是对象本身的数据,即各成员变量的值

3、对齐填充部分只是让实例数据占用的内存空间是8的倍数,无实际意义

 

对象的访问方式

对象创建之后,在java虚拟机栈中进行访问,有2种访问方式

  • 直接指针访问:虚拟机栈的局部变量表中存储对象的引用(reference类型),通过引用直接访问对象
  • 句柄访问:用句柄存储对象的引用,句柄放在句柄池中,虚拟机栈的局部变量表中存储对象的句柄,相当于二级指针。句柄池是堆中的一块内存。

直接指针访问效率高,但gc回收对象时效率低;句柄访问效率低,但gc回收对象时效率高。HotSpot虚拟机采用的是直接指针访问。

转载地址:http://fwhlb.baihongyu.com/

你可能感兴趣的文章
白鹭引擎正式支持微信小游戏开发
查看>>
2018年,你所不知道的Jira!
查看>>
2017年,阿里巴巴开源的那些事
查看>>
这有一节价值30美元的AI免费课等您领取
查看>>
推动边缘计算的七项核心技术
查看>>
边缘计算精华问答 | 边缘计算需要IaaS、PaaS、SaaS等服务能力吗?
查看>>
Spark精华问答 | Spark 会替代Hadoop 吗?
查看>>
豆瓣已玩烂,来爬点有逼格的 ——IMDB 电影提升你的品位
查看>>
一部刷爆朋友圈的5G短片,看完才知道5G多暖多重要!
查看>>
要闻君说:华云数据“豪气”收购超融合厂商Maxta;VMware有意“携手”微软Azure云;亚马逊获3亿美元云计算合同...
查看>>
SDN精华问答 | SDN可以做什么?
查看>>
云评测 | 开发者最有用的开源云监控工具有哪些呢? 这7款神器总有一款适合你!...
查看>>
小团队的微服务之路
查看>>
K8S精华问答 | Kubernetes集群不能正常工作,难道是防火墙问题?
查看>>
5G精华问答 | 什么是5G?5G与LTE有什么关系?
查看>>
虎牙直播在微服务改造方面的实践和总结
查看>>
微服务精华问答 | 在使用微服务架构时,您面临哪些挑战?
查看>>
Kubernetes 调度器实现初探
查看>>
边缘计算精华问答 | 边缘计算有哪些应用场景?
查看>>
要闻君说:Synergy Research Group首发云基础设施数据,腾讯云v5一把;京东物流发力5G;厉害!阿里挖走贾扬清...
查看>>