提示:此系列博客为博主个人读书笔记,其作用是总结书中内容,个人理解内存,方便复习使用。博客内容分为:原书内容总结和个人理解内容。
注:本文原书内容为博主个人提炼总结内容,方便突出要点。
文章目录
前言
以最常用的虚拟机HotSpot和最常用的内存区域Java堆为例,深入探讨一下HotSpot虚拟机在Java堆中对象分配、布局和访问的全过程。
2.3.1 对象的创建
【文中讨论的对象限于普通Java对象,不包括数组和Class对象等】
当Java虚拟机遇到一条字节码new指令时
【个解】这是书中原文,而且是开头。我当时读书的时候,看了new关键字,复读才发现问题。有一说一,确实不太适合没有啥基础的读,像我这种半路出家的野路子,上来就懵了。
对于.class文件可以通过 javap -v 来查看字节码指令。
Java编译器会在遇到new关键字的地方同时生成这两条字节码指令(new指令 和 invokespecial指令)但如果不用new关键字可能不一定。
new指令
类加载
new指令:在常量池中找到类的符号引用,检查类是否有过类加载过程(如无先加载),虚拟机分配对象内存(内存大小在类加载过程确定)。
规划内存
规划内存方式:
指针碰撞(Bump The Pointer):空闲内存在一边,中间指针,使用内存在另一边,分配内存仅仅挪动指针即可。要求java堆中内存使用规整。
空闲列表”(Free List):虚拟机维护一个列表,记录那些内存可用,分配时从列表上找到足够大空间分配,并更新列表。java堆中内存不规整
分配方式 取决于 java堆是否规格 取决于 垃圾回收器是否带有空间压缩整理(Compact)的能力。
使用Serial、ParNew等带压缩整理过程的收集器时:指针碰撞
CMS这种基于清除(Sweep)算法的收集器时理论上:空闲列表
【注解】强调“理论上”是因为在CMS的实现里面,为了能在多数情况下分配得更快,设计了一个叫作Linear Allocation Buffer的分配缓冲区,通过空闲 列表拿到一大块分配缓冲区之后,在它里面仍然可以使用指针碰撞方式
来分配。
指针并发问题:
1.CAS
2.每个线程在堆中预设自有内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB。本地缓冲区用完了,分配新的缓存区时需要同步锁定。
初始化为零值
虚拟机将分配到的内存空间(但不包括对象头)都初始化为零值,如果使用了TLAB的话,这一项工作也可以提前至TLAB分配时顺便进行。
【个解】零值 — 默认值
对象头设置
【个解】此部分原文说稍后再聊
截止目前从一个虚拟机的角度来说,一个对象已经产生了。对于new指令也结束了。但是对于java程序员,这时候的对象还少一步构造器。
invokespecial指令
执行(),初始化构造器。
【个解】具体的指令作用书中后面有介绍
invokespecial指令:用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法。
2.3.2 对象的内存布局
HotSpot虚拟机,对象在堆中划分为三个部分:
对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
对象头
对象头两类信息。对象头里的信息是与对象自身定义的数据无关的额外存储成本。
第一类 Mark Word:存储对象自身的运行时数据。例如:哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。32个比特和64个比特,是动态的数据结构。
【个解】动态的数据结构是指:根据不同的状态复用自己的存储空间。这里借用寒食大老哥的 由浅入深java并发 的一张图
这里额外提一嘴,原文中有个Bitmap,百度后发现好想法。假如存入不重复的n个值,怎样节省空间?他的思想是:索引当值,把 字节 当做数组,一个 字节 有 8位,位上是0表示不存在,1则存在,位所在的索引则为它的值。
另一类 类型指针:对象指向类型元数据的指针,但并不是所有虚拟机都会有。
如果是数组对象,还会有一块表示数组长度的地方。
实例数据
实例数据:对象真正存储的数据,即为的字段内存,无论是父类,子类。字段存储顺序会根据类型会有影响,HotSpot虚拟机默认的分配顺序为longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers,OOPs)相同宽度放到一起,但父类变量出现在子类之前,但子类较窄变量也可插入父类间隙之中,也便节省空间。
对齐填充
HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,对象头部分正好是8字节的倍数(1倍或者2倍),因此,对齐填充,填充 实例数据 部分。
2.3.3 对象访问定位
在使用对象时,会通过栈上的reference数据访问到堆上的具体对象。在《Java虚拟机规
范》中规定指向对象,并没有具体规定怎么定位,访问堆中对象的具体位置。主流访问方式有两种:句柄和直接指针
【个解】reference(引用地址)指向堆
句柄
reference -> 句柄 -> 对象数据或类数据
优点:堆内对象地址移动时,只改变句柄中对象地址,不会改动栈。
缺点:访问对象需要两次指向操作。
直接指针
reference -> 对象数据
reference -> 对象数据 -> 类数据
优点:访问对象一次就可
缺点:对象移动同步修改栈指向
HotSpot采用 直接指针
总结
在思考要不要去除,原文总结 和 个人理解部分。当时怎么考虑是因为,不想像别的读书笔记一样是总结,这样别人看,区分不开书中和自己的部分,要是自己理解有误就是罪过大了。笔记的目的,一是加深自己理解,二是此书确实属于不太好读的一类,能让看不下去的人和让不太喜欢看书的人,直接看博客就行。