终于到了重头戏,也到了最消耗脑力的部分了。这里要说明一点,这里提及的只是常见的锁的原理,并不是所有锁原理展示(如Synchronized展示的是对象锁,而不是类锁,网上也基本没有博客详细写类锁的实现原理,但不代表没有)。如Synchronized方法是通过ACC_SYNCHRONIZED进行隐式同步的。
对象在内存中的结构(重点)首先,我们需要正常对象在内存中的结构,才可以继续深入研究。
JVM运行时数据区分为线程共享部分(MetaSpace,堆),线程私有部分(程序计数器,虚拟机栈,本地方法栈)。这部分不清楚的,自行百度或查看我之前有关JVM的笔记。那么堆空间存放的就是数组与类对象。而MetaSpace(原方法区/持久代)主要用于存储类的信息,方法数据,方法代码等。
我知道,没有图,你们是不会看的。
PS:为了偷懒,我放的都是网络图片,如果挂了。嗯,你们就自己百度吧
PS2:如果使用的网络图片存在侵权问题,请联系我,抱歉。
第一张图,简单地表述了在JVM中堆,栈,方法区三者之间的关系
我来说明一下,我们代码中类的信息是保存在方法区中,方法区保存了类信息,如类型信息,字段信息,方法信息,方法表等。简单说,方法区是用来保存类的相关信息的。详见下图:
而堆,用于保存类实例出来的对象。
以hotspot的JVM实现为例,对象在对内存中的数据分为三个部分:
对象头(Header):保存对象信息与状态(重点,后面详细说明)
实例数据(Instance Data):对象真正存储的有效数据(代码定义字段,即对象中的实际数据)
对齐填充(Padding):VM的自动内存管理要求对象起始地址必须是8字节的整数倍(说白了,就是抛弃的内存空间)
简单说明一下,对齐填充的问题,可以理解为系统内存管理中页式内存管理的内存碎片。毕竟内存都是要求整整齐齐,便于管理的。如果还不能理解,举个栗子,正常人规划自己一天的活动,往往是以小时,乃至分钟划分的时间块,而不会划分到秒,乃至微妙。所以为了便于内存管理,那些零头内存就直接填充好了,就像你制定一天的计划, 晚上睡眠的时间可能总是差几分钟那样。如果你还是不能理解,你可以查阅操作系统的内存管理相关知识(各类内存管理的概念,如页式,段式,段页式等)。
如果你原先对JVM有一定认识,却理解不深的话,可能就有点迷糊了。
Java对象中的实例数据部分存储对象的实际数据,什么是对象的实际数据?这些数据与虚拟机栈中的局部变量表中的数据又有什么区别?
且听我给你编,啊呸,我给你说明。为了便于理解,插入图片
Java对象中所谓的实际数据就是属于对象中的各个变量(属于对象的各个变量不包括函数方法中的变量,具体后面会谈到)。这里有两点需要注意:
代码中是由实际变量与引用变量的概念之分的。实际变量就是实际保存值的变量,而引用变量是一个类似C语言指针的存在,它不保存目标值,而是保存实际变量的引用地址。如果你还是没法理解,你可以通过数组实验,或认识Netty零拷贝,原型模式等方法去了解相关概念,增强积累。
内存中对象存储的变量多为引用变量。