深入理解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 ...
随机推荐
- CDC(跨时钟域)和亚稳态
- Python_爬虫_基础
1.urllib 和 Xpath的区别与联系 from urllib import request from lxml import etree from bs4 import BeautifulS ...
- flink1.10版本StreamGraph生成过程分析
1.StreamGraph本质 本质就是按照用程序代码的执行顺序构建出来的用于向执行环境传输的流式图,并且可以支持可视化展示给用户的一种数据结构. 2.StreamGraph.StreamNode和S ...
- [LeetCode题解]876. 链表的中间结点 | 快慢指针
解题思路 使用快慢指针.这里要注意的是,while 的条件会影响当中间节点有两个时,slow 指向的是第一个,还是第二个节点. // 返回的是第一个 while(fast.next != null & ...
- 前后端分离之前端vue
npm install --global vue-clivue init webpack my-project cd my-project npm install npm run dev ...
- 下载并破解IntelliJ IDEA(2017)
idea的下载:https://www.jetbrains.com/idea/download/#section=windows 破解链接:http://blog.csdn.net/nn_jbrs/a ...
- Shamir秘密共享方案 (Python)
Shamir's Secret Sharing scheme is an important cryptographic algorithm that allows private informati ...
- C++基础知识篇:C++ 变量作用域
作用域是程序的一个区域,一般来说有三个地方可以定义变量: 在函数或一个代码块内部声明的变量,称为局部变量. 在函数参数的定义中声明的变量,称为形式参数. 在所有函数外部声明的变量,称为全局变量. 我们 ...
- CentOS下Python尝试
打印一个爱心 #猴赛雷 print'\n'.join([''.join([('AndyLove'[(x-y)%8]if((x*0.05)**2+(y*0.1)**2-1)**3-(x*0.05)**2 ...
- Alpha冲刺-第四次冲刺笔记
Alpha冲刺-冲刺笔记 这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzzcxy/2018SE2 这个作业要求在哪里 https://edu.cnblogs. ...