深入理解Java虚拟机(二)——HotSpot对象创建、内存、访问
对象的创建
虚拟机遇到一条字节码new指令时,开始对象创建过程。
- 首先去检查这个指令的参数是否能在常量池定位到一个类的符号引用;
- 检查这个符号引用代表的类是否已被加载、解析和初始化,如果没有就必须执行相应的类加载过程;
- 根据方法区中该类的信息确定对象的所需空间大小;
- 虚拟机为新生对象分配内存;
- 将对象实例的内存(不包括对象头)进行初始化为零值;
- 配置对象头的信息;
- 调用对象的构造函数进行初始化。
这样,一个真正可用的对象被完全构造出来了。
多线程中,引用指向对象的内存空间和对象初始化操作可能会出现重排序,这样会导致对象没有初始化就被其他线程使用了,就会出错。方法这种情况,就需要将对象声明为volatile。《Java并发编程艺术》
分配内存方法
虚拟机为新生对象分配内存有两种方法:
- 碰撞指针:如果虚拟机垃圾收集器采用的复制算法或者标记-整理算法,那么堆中空闲内存和已使用过的内存是连续放在一块的,二者中间存在一个指针作为分界点的指示器。这样分配内存的时候,只需要将指针向空间区域挪动一段与对象大小相同的距离,这种方式就是指针碰撞。
- 空闲列表 :如果虚拟机垃圾收集器采用的标记-清除算法:那么堆中的空闲内存和已使用内存不是连续的,是交错的,虚拟机通过维护一张列表来记录可用内存块,在分配内存的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表,这种分配方式就是空闲列表。
所以,虚拟机采用何种内存分配方法,取决于其所用的垃圾回收算法。
并发时对象分配
解决并发时对象分配的问题:即使仅仅修改一个指针所指向的位置,在并发情况下也并不是线程安全的,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。
两种方案可以解决:
- 通过CAS配上失败重试的方法可以保证操作的原子性。
- 通过给每个线程一个本地线程分配缓冲区,当线程需要分配内存时,就在它相应的缓冲区分配,当缓冲区耗尽,就进行同步锁定。
对象的内存模型
对象在堆内存中可以划分为三个部分:
- 对象头
- 实例数据
- 对齐补充
1.对象头
对象头包括两类信息:
- 第一类用于存储对象自身的运行时数据:例如哈希码、GC分代年龄、锁状态、线程持有的锁、偏向线程ID、偏向时间戳等。
- 可能还存在第二类是类型指针,就是对象指向它的类型元数据指针,虚拟机通过这个指针来确定该对象是哪个类的实例。
- 如果是Java数组对象,对象头重还必须有一块数据用于记录数组的长度。
2.实例数据
是对象真正存储的有效信息,就是在程序代码里面定义的给中类型的字段内容,成员变量的值,其中包含父类的成员变量和当前类定义的变量。
3.对齐填充
作用只是占位符。并不是必然存在的。
HotSpot要求对象的起始地址必须是8子节的整数倍,所以任何对象的大小都必须是8子节的整数倍。对象头已经被设计为8字节的整数倍,如果实例数据没有对齐,就需要通过对齐填充部分进行补全。
对象访问定位
Java程序会通过栈上的reference数据来操作堆上的具体对象。所谓reference类型就是一个指向对象的引用。
主流的访问方式有两种:
- 使用句柄访问:
堆中划分出一块内存作为句柄池,用于存放对象实例数据和类型数据各自的地址信息。Java栈中存放的引用指向句柄池中对象的句柄地址。 - 直接指针访问:
引用类型指向的是对象的地址,不需要句柄池直接访问对象。但是对象的内存中就需要放置访问类型数据的相关地址信息。
比较:
- 句柄访问的好处是句柄地址稳定,当对象被垃圾收集器移动时,只会改变句柄池中的实例数据地址,而本地变量表中的引用不需要被修改。
- 直接指针访问的好处就是速度快,节省指针定位的时间开销。HotSpot主要采用这种方式进行访问。
深入理解Java虚拟机(二)——HotSpot对象创建、内存、访问的更多相关文章
- 深入理解Java虚拟机二:垃圾收集与内存分配
垃圾收集:垃圾收集要完成三件事,包括哪些内存需要回收,什么时候回收及如何回收. 1.需要回收的内存判定:没有引用指向原先分配给某个对象的内存时,则该内存是需要回收的垃圾 Java垃圾收集器在对内存进行 ...
- 深入理解Java虚拟机二 阅读笔记
xl_echo编辑整理.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! --- > 以下内容摘抄自 ...
- 深入理解Java虚拟机:垃圾收集器与内存分配策略
目录 3.2 对象已死吗 判断一个对象是否可被回收 引用类型 finalize() 回收方法区 3.3. 垃圾收集算法 1.Mark-Sweep(标记-清除)算法 2.Copying(复制)算法 3. ...
- 《深入理解Java虚拟机》学习笔记之内存分配
JVM在执行Java程序的过程中会把它所管理的内存划分若干个不同的数据区域,如下图: 大致可以分为两类:线程私有区域和线程共享区域. 线程私有区域 程序计数器(Program Counter Regi ...
- 《深入理解java虚拟机》读书笔记1--java内存区域
Java内存管理 本文主要介绍Java虚拟机运行时的内存区域是如何划分的.Java对象的创建过程.Java对象的内存布局.Java对象的访问定位 一:运行时区域划分 主要可以分为以下 几个: 程序计数 ...
- java虚拟机(三)--HotSpot 对象
普通对象的创建(不包括数组和class对象): 当虚拟机遇到new指令时,会在常量池中检查是否包含这个类的符号引用(全限定名),通过这个确定是否经过类加载的过程,如果true,为该 对象分配内存,对象 ...
- 深入理解Java虚拟机(四)——HotSpot垃圾收集器详解
垃圾收集器 新生代收集器 1.Serial收集器 特点: 单线程工作,收集的时候就会停止其他所有工作线程,用户不可知不可控,会使得用户界面出现停顿. 简单高效,是所有收集器中额外内存消耗最少的. 没有 ...
- 深入理解Java虚拟机二之Java内存区域与内存溢出异常
运行时数据区域 1.线程独有的内存区域 PROGRAM COUNTER REGISTER 程序计数器 程序计数器空间较小,是当前线程执行字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值 ...
- 深入理解Java虚拟机笔记——垃圾收集器与内存分配策略
目录 判断对象是否死亡 引用计数器算法 可达性分析算法 各种引用 回收方法区 垃圾收集算法 标记-清除算法 复制算法 标记-整理算法 分代收集算法 HotSpot算法实现 枚举根节点 GC停顿(Sto ...
随机推荐
- 头秃了,二十三张图带你从源码了解Spring Boot 的启动流程~
持续原创输出,点击上方蓝字关注我 目录 前言 源码版本 从哪入手? 源码如何切分? 如何创建SpringApplication? 设置应用类型 设置初始化器(Initializer) 设置监听器(Li ...
- 查看ceph集群被哪些客户端连接
前言 我们在使用集群的时候,一般来说比较关注的是后台的集群的状态,但是在做一些更人性化的管理功能的时候,就需要考虑到更多的细节 本篇就是其中的一个点,查询ceph被哪些客户端连接了 实践 从接口上来说 ...
- elementui checkbox复选框实现层级联动
使用elementui 实现复选框的层级联动,可能我的表述不准确,先上一个效果图. 实际开发中可能遇到这样的场景,当选择高一层级的复选框时它包含的低级的复选框就不需要再勾选,需要默认选中并且禁止选用. ...
- 网页中Office和pdf相关文件导出
最近被派去维护和开发一些做了一半.年久失修的项目.有一部分内容是关于word文件导出,顺带着把excel.pdf文件的导出也调研下吧,我想未来开发我应该会遇到的,遂做了下笔记分享给需要的人. 由于项目 ...
- kali 更新msf
用leafpad打开,方便复制粘贴 leafpad /etc/apt/sources.list 然后复制下面的源覆盖原本的 deb http://mirrors.ustc.edu.cn/kali ka ...
- web安全原理-文件包含漏洞
前言 起来吃完早饭就开始刷攻防世界的题,一个简单的文件包含题我竟然都做不出来我服了 拿出买的书开始从头学习总结文件包含漏洞! 一.文件包含漏洞 文件包含漏洞 文件包含函数的参数没有经过过滤或者严格的 ...
- SpringBoot使用策略模式+工厂模式
为了防止大量的if...else...或switch case代码的出现,可以使用策略模式+工厂模式进行优化. 在我的项目当中,报表繁多,所以尝试了这种方式进行优化报表的架构.代码很简单,如下: Fa ...
- 【建议收藏】一份阿里大牛花了三天整理出来的XML学习笔记,写的非常详细
1. 什么是XML? XML 指可扩展标记语言(EXtensible Markup Language)XML 是一种标记语言,很类似 HTMLXML 的设计宗旨是传输数据,而非显示数据XML 标签没有 ...
- 刚安装好的MathType怎么使用
对于刚接触公式编辑器的新手来说,难免会存在很多疑问:如何使用刚安装好的Word公式编辑器?安装好公式编辑器之后,我们在哪里找到这个工具呢?下面就针对大家的这些疑问,来给大家介绍下首次使用MathTyp ...
- PHP获取数组中重复值的键值
$array = array ( 0=>'a', 1=>'b', 2=>'a', 5=>'b', 6=>'c', 40=>'d' ); $keyarr =[];$r ...