-
2019-08-11 19:04:04
1.1面向过程与面向对象的区别
面向对象是一种现在最为流行的程序设计方法 。将面向过程与面向对象进行比较。
首先这两个种都是一种编程思维, 在一些简单的动作看来面向过程可能更加容易实现,它不用创建对象来进行实现,执行几步或一步完成。但是面对复杂动作面向对象来实现可能更加简单。二者之间做项目谁最简单不是绝对的
面向过程(pop):以需要做的事为目标为进行,侧重点为目的。
举个例子(洗衣服):1.拿起脏衣服
2.丢进洗衣机
3.倒入洗衣液
4.开启洗衣机
面向对象(oop):将需要做的事,分为多个模块(对象),侧重点在于受影响的对象。
将洗衣服这个过程,进行封装,
1.创建人的对象,然后人拿起脏衣服衣服丢进洗衣机
2.创建洗衣服的对象,判断是否有脏衣服进入洗衣机,然后开启洗衣服模式。
在这个例子中,不难看出,二者的侧重点完全不一样。前者为一步一步实现,然而后者是针对这个过程从产生出执行这些动作的事物来,从而达到目的。在java中这些事物我们称为对象。
1.2 什么是类
我们在生活中将狮子,豹子,狼归类为一类,我们称为动物类。它是一个客观事物,没有一个具体的提现,一个抽象的存在,却综合和大部分动物的特征,当我们听到动物时,往往会联想到前面提到的这些,然后会想到它们是肉食动物,群居动物(这些都是他们的属性)它们共有着捕食猎物的行为(看做成一个方法),这些都被动物所概括。
那当我们联想到人呢。我们会联想到名字,头发,以及肤色。在java中我们把class标识作为一个类,下面是我的person类,它有着名字,头发,皮肤的特征,并且有一个行为eat()1.3什么是对象?
那么,我们该怎么执行这些代码呢,在生活中我们不会说人类去吃饭了,而是会说xx去吃饭了,这个xx是具体的某个人,在java中我们把这个xx定义为对象,它是一种客观事物的具体表现形式,在java编程思想(Thinking In java )中,指出,万物皆对象,所有事物都可能看做为一个对象,一类事物的分类,一类事物的具体表现形式,它有着自己类中具体的属性,以及行为。对象是类的实例
这些属性和行为都来自于我定义的类的代码,但是却没有写明名字叫什么,头发什么颜色。现在我需要从这个person类中创建一个具体的表现对象
请看下面的代码
从person中创建了这个对象,并且执行了eat()方法,在控制台进行了输出
接下来对zhangsan的属性进行初始化总结:
通过代码可以看出, 我在person的类中创建了一个叫zhangsan的对象,他是person类的实例,并且根据它所在的类进行了属性的初始化赋值,执行了这个类都的方法。那么这个类有一个对象(实例)名字叫张三,有着黄色的皮肤,和黑色的头发,并且会吃饭。
类:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该 类事物。 现实中,描述一类事物:
属性:就是该事物的状态信息。 行为:就是该事物能够做什么。 举例:人。 属性:名字、体重、年龄、颜色。 行为:走、跑、叫。
对象:是一类事物的具体体现。对象是类的一个实例(对象并不是找个女朋友),必然具备该类事物的属性 和行为。 现实中,一类事物的一个实例:一只小猫。 举例:一个人。
属性:名字,头发,皮肤
行为:吃饭。
类与对象的关系 类是对一类事物的描述,是抽象的。 对象是一类事物的实例,是具体的。
类是对象的模板,对象是类的实体。更多相关内容 -
Java对象的对象头到底是什么?
2020-08-17 14:54:58一:对象头 HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。 HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分...目录
Java对象保存在内存中时,由以下三部分组成:
1,对象头
2,实例数据
3,对齐填充字节
一,对象头
java的对象头由以下三部分组成:
1,Mark Word
2,指向类的指针
3,数组长度(只有数组对象才有)
1,Mark Word
Mark Word记录了对象和锁有关的信息,当这个对象被synchronized关键字当成同步锁时,围绕这个锁的一系列操作都和Mark Word有关。
Mark Word在32位JVM中的长度是32bit,在64位JVM中长度是64bit。
Mark Word在不同的锁状态下存储的内容不同,在32位JVM中是这么存的:
锁状态
25bit
4bit
1bit
2bit
23bit
2bit
是否偏向锁
锁标志位
无锁
对象的HashCode
分代年龄
0
01
偏向锁
线程ID
Epoch
分代年龄
1
01
轻量级锁
指向栈中锁记录的指针
00
重量级锁
指向重量级锁的指针
10
GC标记
空
11
其中无锁和偏向锁的锁标志位都是01,只是在前面的1bit区分了这是无锁状态还是偏向锁状态。
JDK1.6以后的版本在处理同步锁时存在锁升级的概念,JVM对于同步锁的处理是从偏向锁开始的,随着竞争越来越激烈,处理方式从偏向锁升级到轻量级锁,最终升级到重量级锁。
JVM一般是这样使用锁和Mark Word的:
1,当没有被当成锁时,这就是一个普通的对象,Mark Word记录对象的HashCode,锁标志位是01,是否偏向锁那一位是0。
2,当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。
3,当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。
4,当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是Mark Word中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把Mark Word里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。
5,偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把Mark Word中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。
6,轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。
7,自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。
2,指向类的指针
该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。
Java对象的类数据保存在方法区。
3,数组长度
只有数组对象保存了这部分数据。
该数据在32位和64位JVM中长度都是32bit。
二,实例数据
对象的实例数据就是在java代码中能看到的属性和他们的值。
三,对齐填充字节
因为JVM要求java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8bit的倍数,没有特别的功能。
一:对象头
HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。
对象需要存储的运行时数据很多,其实已经超出了32、64位Bitmap结构所能记录的限度,但是对象头信息是与对象自身定义的数据无关的额 外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。例如在32位的HotSpot虚拟机 中对象未被锁定的状态下,Mark Word的32个Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象分代年龄,2Bits用于存储锁标志 位,1Bit固定为0,在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容如下表所示。
对象头的另外一部分是类型指针,即是对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说查找对象的元数据信息并不一定要经过对象本身。另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。
这里要特别关注的是锁标志位, 锁标志位与是否偏向锁对应到唯一的锁状态。
所以锁的状态保存在对象头中,所以再理解
- Synchronized锁的到底是什么, 锁住的是代码还是对象)(答案锁的是对象)?
- java中锁,锁的是对象,它是怎么实现的?
这两个问题,就好懂了!
二:锁的状态
锁的状态总共有四种:无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级)。JDK 1.6中默认是开启偏向锁和轻量级锁的,我们也可以通过-XX:-UseBiasedLocking来禁用偏向锁。
1、轻量级锁的加锁过程
(1)在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,官方称之为 Displaced Mark Word。这时候线程堆栈与对象头的状态如图2.1所示。
(2)拷贝对象头中的Mark Word复制到锁记录中。
(3)拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock record里的owner指针指向object mark word。如果更新成功,则执行步骤(4),否则执行步骤(5)。
(4)如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如图2.2所示。
(5)如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。 而当前线程便尝试使用自旋来获取锁,自旋就是为了不让线程阻塞,而采用循环去获取锁的过程。
图2.1 轻量级锁CAS操作之前堆栈与对象的状态
图2.2 轻量级锁CAS操作之后堆栈与对象的状态
三、偏向锁
引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS原子指令的性能消耗)。上面说过,轻量级锁是为了在线程交替执行同步块时提高性能,而偏向锁则是在只有一个线程执行同步块时进一步提高性能。
1、偏向锁获取过程:
(1)访问Mark Word中偏向锁的标识是否设置成1,锁标志位是否为01——确认为可偏向状态。
(2)如果为可偏向状态,则测试线程ID是否指向当前线程,如果是,进入步骤(5),否则进入步骤(3)。
(3)如果线程ID并未指向当前线程,则通过CAS操作竞争锁。如果竞争成功,则将Mark Word中线程ID设置为当前线程ID,然后执行(5);如果竞争失败,执行(4)。
(4)如果CAS获取偏向锁失败,则表示有竞争。当到达全局安全点(safepoint)时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码。
(5)执行同步代码。
2、偏向锁的释放:
偏向锁的撤销在上述第四步骤中有提到。偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动去释放偏向锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态,撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态。
3、重量级锁、轻量级锁和偏向锁之间转换
该图主要是对上述内容的总结,如果对上述内容有较好的了解的话,该图应该很容易看懂。
参考资料:
http://blog.csdn.NET/u010723709/article/details/50341631
http://www.cnblogs.com/paddix/p/5405678.html
http://www.cnblogs.com/lingepeiyong/archive/2012/10/30/2745973.html
-
Java虚拟机中对象探秘--对象头创建、对象头、对象锁、synchoronized底层实现
2018-05-18 16:26:01今天看别人的博客,讲到面试相关的问题,其中有一个知识点是:synchronized关键字,Java对象头、Markword概念、synchronized底层实现,monitorenter和monitorexit指令,一脸蒙逼,虽然早期把《深入理解Java虚拟机》...今天看别人的博客,讲到面试相关的问题,其中有一个知识点是:synchronized关键字,Java对象头、Markword概念、synchronized底层实现,monitorenter和monitorexit指令,一脸蒙逼,虽然早期把《深入理解Java虚拟机》这本书看过一遍了,可是发现当时只是走马观花的看,对于这些知识点,还是不知道,今天特意把Java对象这一节再次重新读一遍,加深记忆。对上面这些知识点就立马懂了。
1. 对象的创建
创建对象(如克隆、反序列化)通常仅仅一个new关键字,但在虚拟机中,对象的创建的过程需要如下步骤:
1.1 类加载检查
先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析、初始化过,若没有,则必须先执行相应的类加载过程。
1.2 为新生对象分配内存
对象所需内存大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从java堆中划分出来。根据java堆内存是否绝对规整,划分方法不同:
1)指针碰撞(Bump the Pointer): Java堆中内存绝对规整(所有用过的内存放在一边,空闲的内存放在另一边,中间放一个指针作为分界点的指示器),所分配的内存仅需要把指针向空闲空间那边挪动一段与对象大小相等的距离。
2)空闲列表(Free List),Java堆中内存并不规整(已使用的内存和空闲的内存相互交错),虚拟机通过维护一个记录哪些内存可用的列表,在分配时从列表中找到一块中够大的空间划分给对象实例,并更新列表上的记录。
分配方式由Java堆是否规整决定,而是否规整由所采用的垃圾惧器是否带有压缩整理功能决定。
【注】由于对象创建在虚拟机中是非常频繁的行为,仅修改一个指针所指向的位置,并发情况下也不是线程安全的,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存情况。解决方案有两种:
1)对分配内存空间的动作进行同步处理,虚拟机采用CAS配上失败重试方式保证更新操作的原子性;
2)本地线程分配缓冲(Thread Local Allocation Buffer,TLAB),给每个线程在Java堆中预先分配一小块内存,哪个线程要分配内存,就在哪个线程的TLAB上分配,TLAB用完分配新的时才需要同步锁定。可以通过-XX:+/-UseTLAB参数来设定是否使用TLAB。
1.3 将分配的内存空间初始化为零值(不包括对象头)
若使用TLAB,可以提前至TLAB分配时进行。这一步操作保证了对象实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
1.4 对对象头进行必要设置
如这个对象是哪个类的实例,如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。根据虚拟机当前的运行状态不同,如是否启用偏向锁等,对象头会有不同的设置方式。关于对象头见下面 2.1对象头。1.5 执行<init>方法
把对象按照程序员意愿进行初始化,即执行<init>方法。
2. 对象的内存布局
在HotSpot虚拟机中,对象在内存中存储的布局可以分成三块:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
2.1 对象头
对象头分成两部分信息:
2.1.1 存储对象自身的运行时数据
如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。这部分数据长度在32位和63位的虚拟机中分别为32bit和64bit,官方称它为"Makr Word"。对象要存储的运行时数据很多,其实已超出32位、64位Bitmap结构所以记录的限度,但对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息。它会根据对象状态复用自己的存储空间。
下面表示32位HotSpot虚拟机中,如果对象处理未被锁定状态,Mark Word的32位空间分成:25bit用于存储对象哈希码,4bit用于存储对象分代年龄,2bit用于存储锁标志位,1bit固定为0,而在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容如下:
2.1.1 类型指针
类型指针即代表对象指向它的类元数据的指针,虚拟机通过这个指针来确定该对象是哪个类的实例。但查找对象的元数据信息并不一定要经过对象本身。如果对象是一个Java数组,在对象头中还须有一块用于记录数组长度的数据,因为虚拟机可通过普通Java对象的元数据信息确定Java对象的大小,但从数组的元数据中无法确定数组的大小。
2.2 实例数据
真正存储有效信息,即程序代码中所定义的各种类型的字段内容,包括父类继承下来的和子类定义的。这部分存储顺序受虚拟机分配策略参数(FiledsAllocationStyle)和字段在Java源码中定义的顺序的影响。HotSpot虚拟机默认分配策略为longs/double、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers),从分配策略可以看出,相同宽度的字段总是被分配到一起。在此前提条件下,父类中定义的变量会出在子类之前,若CompactFileds的参数值为true,那么子类中较窄的变量也可能会插到父类变量的空隙中。
2.3 对齐填充
不是必然存在的,仅起着占位符作用。HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,即对象大小必须是8字节的整数倍,而对象头正好是8字节的整数倍,而对象实例数据部分没有对齐,需要通过对齐填充来补全。
3. 对象的访问定位
建立对象是为了使用对象,Java程序需要通过栈上的引用数据来操作堆上的具体对象,由于reference类型在Java虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该通过何种方式去定位、访问堆中对象的具体位置,故对象访问方式取决于虚拟机实现。目前主要有两种方式:
3.1 使用句柄
Java堆中会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,句柄中包含了对象实例数据与类型数据各自的具体地址信息。如下图所示:
3.2 直接指针
Java堆对象的布局中需要放置访问类型数据的相关信息,reference中存储的直接就是对象的地址,如下图所示:
4. synchronized底层实现原理
4.1 synchronized使用场景
synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。synchronized的作用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见(3)有效解决重排序问题。从语法上讲,它总共有三种使用场合:
修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
4.2 synchronized底层实现原理
由2.1介绍Java对象头知道,在Java对象头中有2bit用来标识对象存储锁的,可知锁对象是存储在Java对象头里的。synchronized的对象锁对应的重量级锁,锁标识位为10, 其中指针指向的monitor对象(管程或监视器锁)的起始地址。每个对象都存在着一个monitor与之关联,对象与其monitor间关系存在多种实现方式,monitor可以与对象一起创建或当线程试图获取对象锁时自动生成,但当一个monitor被某个线程持有后,它便处理锁定状态。
4.2.1 synchronized作用于实例方法
synchronied修饰实例对象中的实例方法,不包括静态方法,如下:
public synchronized void increase() { i++; }
使用javap反编译后的字节码如下:
//省略没必要的字节码 //==================increase方法====================== public synchronized void increase(); descriptor: ()V //方法标识ACC_PUBLIC代表public修饰,ACC_SYNCHRONIZED指明该方法为同步方法 flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field i:I 5: iconst_1 6: iadd 7: putfield #2 // Field i:I 10: return LineNumberTable: line 12: 0 line 13: 10 }
从字节码可知JVM采用ACC_SYNCHRONIZED标识指明该方法是一个同步方法,从而执行相应的同步调用。
4.2.2 synchronized作用于代码块
public class SyncCodeBlock { public int i; public void syncTask(){ //同步代码库 synchronized (this){ i++; } } }
使用javap反编译后得到字节码如下:
//===========主要看看syncTask方法实现================ public void syncTask(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter //注意此处,进入同步方法 4: aload_0 5: dup 6: getfield #2 // Field i:I 9: iconst_1 10: iadd 11: putfield #2 // Field i:I 14: aload_1 15: monitorexit //注意此处,退出同步方法 16: goto 24 19: astore_2 20: aload_1 21: monitorexit //注意此处,退出同步方法 22: aload_2 23: athrow 24: return Exception table: //省略其他字节码....... }
从字节码中可知同步语句块实现是使用monitorenter和monitorexit指令,其中monitorenter指令指向同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置,当执行monitorenter指令时,当前线程将试图获取objectref(对象锁)所对应的monitor的持有权。直到线程执行完毕,monitorexit指令执行,释放monitor。
【后记】根据前面讲的Java对象头知识,我们知道还有偏向锁、轻量级锁,这些都是相对于synchronized这样的重量级锁的优化,但这不是本文关注的重点,想了解更多,可以查看这篇文章深入理解Java并发之synchronized实现原理
-
JVM(5)之对象创建过程+对象头具体包括什么+对象定位
2022-03-16 15:11:06首先在创建对应的时候如果对象不存在肯定是涉及到class文件的加载,class文件的加载需要经历:加载 ->连接->初始化。 class loading:加载 class linking:连接,连接又有具体的三步,分别是:验证(验证包括...一、一线互联网面试题
首先先看下下面的面试题:
二、对象的创建过程
首先在创建对应的时候如果对象不存在肯定是涉及到class文件的加载,class文件的加载需要经历:加载 ->连接->初始化。
- class loading:加载
- class linking:连接,连接又有具体的三步,分别是:验证(验证包括:calss文件格式验证、meta元数据验证等)、准备、解析:对应会给变量赋默认值。
- class initializing 静态初始化的过程,这里会给变量赋值初始值;
- 申请对象内存
- 成员变量赋默认值;
- 调用构造方法<init>,这里会给成员变量顺序赋初始值,执行构造方法语句:首先去调用super。
三、对象在内存中的存储布局
2.1观察虚拟机配置的命令:
java -XX:+PrintCommandLineFlags -version
这个就是我本机默认的虚拟机配置参数:
2.2普通对象
内存里的一个Java对象分为三部分:对象头,实例数据,对齐、class Pointer 类指针。
- 对象头:markword 8Bytes 用于标记锁信息、GC信息、IdentityHashCode等。
- class Pointer 类指针:jvm开启内存压缩(-XX:+UseCompressedClassPointer),4字节。不开启,8字节。默认开启,用于标记该对象是哪个Class的实例 例如:Object.class。
- 实例数据(instance data):包括对象的所有成员变量,大小由各个成员变量决定,如果没有成员变量,则这一块为空,基本类型 (int,float 4字节 long,double 8字节,char,short 2字节,byte 1字节 ,boolean 实际大小1bit 占用1字节)。引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节,Oops Ordinary Object Pointers;
- Padding对齐:8的倍数(因为读取的时候不是按直接读的,是读的块,一般是8个字节的倍数,这样效率会更高);
2.3数组对象
数组对象跟普通对象是差不多的,只不过比普通对象多出了一块数据:数组长度;
数组长度:4字节 标记数组有多少个元素;
下面做一个实验,做这个实验,首先我们需要自己做一个工具jar,实验:利用java agent(class文件到内存之间的代理,这个代理得自己去实现)的机制。下面补充了种用JOL工具的,比较简单。
1、创建文件ObjectSizeAgent(类似到内存前有个代理)
public class ObjectSizeAgent { // 类似调弦的作用 private static Instrumentation inst; public static void premain(String agentArgs, Instrumentation _inst) { inst = _inst; } public static long sizeOf(Object o) { return inst.getObjectSize(o); } }
2、src目录下创建META-INF/MANIFEST.MF
这里可以只要Premain-Class: com.mashibing.jvm.agent.ObjectSizeAgent;
Manifest-Version: 1.0
Created-By: mashibing.com
Premain-Class: com.mashibing.jvm.agent.ObjectSizeAgent3、打包jar文件
4、在需要使用该Agent Jar的项目中引入该Jar包 project structure - project settings - library 添加该jar包
5、运行时需要该Agent Jar的类,加入参数:
执行用哪个jar文件当作代理来运行我的虚拟机:
-javaagent:C:\work\ijprojects\ObjectSize\out\artifacts\ObjectSize_jar\ObjectSize.jar
6、如何使用该类:
package com.mashibing.jvm.c3_jmm; import com.mashibing.jvm.agent.ObjectSizeAgent; public class T03_SizeOfAnObject { public static void main(String[] args) { System.out.println(ObjectSizeAgent.sizeOf(new Object())); System.out.println(ObjectSizeAgent.sizeOf(new int[] {})); System.out.println(ObjectSizeAgent.sizeOf(new P())); } private static class P { //8 _markword //4 _oop指针 int id; //4 String name; //4 int age; //4 byte b1; //1 byte b2; //1 Object o; //4 byte b3; //1 } }
测出来 object:16个字节 object大小分析:对象头是8个字节,本来应该是8个字节(64位的)因为默认打开时压缩的,所以加上被压缩成4个字节(class_pointer ),一共是12字节,再加上有4字节的填充数据,一共16字节;
3、对象头包括什么
1.8的实现,C++文件,去看Hotspot源码。
HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。
对象需要存储的运行时数据很多,其实已经超出了32、64位Bitmap结构所能记录的限度,但是对象头信息是与对象自身定义的数据无关的额 外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。例如在32位的HotSpot虚拟机 中对象未被锁定的状态下,Mark Word的32个Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象分代年龄,2Bits用于存储锁标志 位,1Bit固定为0,在其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的存储内容如下表所示。
-
Java的对象头
2021-03-09 22:06:25## Java对象模型回顾每一个Java类,在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类。...这里提到的对象头到底是什么呢?```class oopDesc {friend class V... -
Java的对象头和对象组成详解
2018-10-10 18:56:35一,对象头 1,Mark Word 2,指向类的指针 3,数组长度 二,实例数据 三,对齐填充字节 Java对象保存在内存中时,由以下三部分组成: 1,对象头 2,实例数据 3,对齐填充字节 一,对象头 java的对象头... -
什么是面向对象编程
2020-01-12 18:57:20本文关键字:面向对象、面向过程、面向过程编程语言、面向对象编程语言。说到编程,对于初学者来讲,可能第一想到的就是敲键盘,写代码,做游戏,甚至于会联想到软件破解、网络攻防。另一方面,在学了一些编程的相关... -
什么是面向对象程序设计方法
2021-02-27 20:16:27一、什么是面向对象程序设计? 1、所有的东西都是对象;对象保存着数据,且可对它自身进行操作; 2、可从要解决的问题上提出概念性的组件,然后再程序中将其表达为一个对象,(头、身体、手、腿组成一个人); 3... -
java对象头信息
2019-09-02 14:27:142. 为什么在jdk1.6后,synchronized关键字性能有所提高,为什么会提高?并且很多文章中都说synchronized锁有偏向锁、轻量锁、重量锁等状态? 3. java对象是在那里设置了指针指向对应的方法区中的元数据的? 4. 在... -
什么是面向对象编程?面向对象和面向过程有什么区别?
2019-10-25 16:58:58面向对象的程序设计(Object-Oriented Programming,简记为OOP),是当下最流行的程序设计方式之一,它将程序 -
【俗话编程】什么是对象?
2020-11-22 10:40:57编程中的对象,是一块内存区域,举例Person对象来说,区域头4个字节记录的是他的身高,接下来的4个字节记录的是他的跳跃功能——通常是个函数指针,指向另一块存储函数代码的内存区域——,再下来是2个字节的年龄,... -
Java对象头的内部结构(new Object这个对象里面到底有什么)及锁状态信息【图文】
2020-07-22 10:51:22哈希值:它是一个地址,用于栈对堆空间中对象的引用指向,不然栈是无法找到堆中对象的 GC分代年龄:记录幸存者区对象被GC之后的年龄age,,一般age为15之后下一次GC就会直接进入老年代 锁状态标志:记录一些加锁的... -
JVM - 剖析Java对象头Object Header之对象大小
2020-06-25 16:10:32中大体介绍了Java中 new 对象背后的主要流程,其中对象头的部分,我们仅仅是点到为止,这里我们深入剖一下Object Header的奥秘 。 总览 初始化默认值以后,JVM要对对象进行必要的设置,例如这个对象是哪个类的实例... -
java对象头 MarkWord
2019-06-05 20:41:15原文链接:[https://blog.csdn.net/scdn_cp/article/details/86491792#comments] 我们都知道,Java对象存储在堆(Heap)内存。那么一个Java对象到底包含什么呢?概括起来分为对象头、对象体和对齐字节... -
Java-对象头详解
2021-01-11 14:48:47这里只介绍64位虚拟机的对象头的结构和内容,主要是分为以下四种状态介绍,无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态。 对象头的基本结构 如下图所示 对象的结构和内容我这里就暂且不提了,本章主要讲述的... -
Java对象头与monitor
2020-09-03 15:11:35由于Java面向对象的思想,在JVM中需要大量存储对象,存储时为了实现一些额外的功能,需要在对象中添加一些标记字段用于增强对象功能,这些标记字段组成了对象头。 一、对象头形式 JVM中对象头的方式有以下两种(以32... -
Java对象头
2018-09-04 22:46:03一、对象的内存布局 HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域: 对象头(Header)-&amp;gt;3字宽 Mark Word :存储对象的hashcode或锁信息(1字宽) Class Metadata Address:类型指针... -
【Java对象解析】不得不了解的对象头
2018-03-26 14:53:37一:对象头HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。 HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分用于... -
jsp 有哪些内置对象?作用分别是什么?
2019-06-20 09:47:28作用分别是什么? 序号 内置对象 对应 Servlet 类 作用 1 request javax.servlet.http.HttpServletRequest 客户端的请求信息:Http协议头信息、Cookie、请求参数等 2 response javax.... -
64位JVM的Java对象头详解
2020-02-22 15:42:31关注“Java艺术”一起来充电吧!我们编写一个Java类,编译后会生成.class文件,当类加载器将class文件加载到jvm时,会生成一个Klass类型的对象(c++),称为类描述元数... -
JVM堆中对象的对象头的内部结构细节分析及锁状态【图文】
2020-07-17 12:29:39对象头的内部结构一张图了解所有细节 1、创建了Customer()实例 和 Account()实例 2、对象头里包括:运行时元数据、类型指针、实例数据、对齐填充 ① 运行时元数据里又包括:哈希值(HashCode)、GC分代年龄、锁状态... -
java对象的内存模型详解:内存模型及对象头的奥秘
2021-08-26 15:33:33在面试官的因势利导下,很多人对jvm的内存模型已经耳熟能详,但是对我们经常new 出来的对象,比如new Object(),你了解它的内存模型吗?本篇文章将带你走进对象内部,真正去了解这... // 对象头 volatile markOop _ma -
Java中创建对象的六个步骤 细分后(new关键字)对象头详细介绍
2020-07-16 22:31:39要看的懂对象的创建过程,首先你得有对Java虚拟机和Java基础以及JUC很是熟悉,比如类的加载过程,CAS、多线程、JVM的GC等等 首先好看一个图,我大概会根据图中的内容来分享这六个步骤(其实几个步骤不重要,只要包括... -
深入理解Java的对象头mark word
2020-06-19 12:49:55上一篇博客我们编译了Linux源码来证明了Java中有偏向锁,但是我们从周志明大佬的《深入理解java虚拟机》的书中知道,我们可以通过分析Java对象头中MarkWord来查看是那种锁,下面是32位JVM的对象中的Mark Word图,... -
头歌实践教学平台:Java面向对象 - 类与对象
2022-01-16 17:01:14第1关:什么是类,如何创建类 本关任务:创建一个类和一个对象,调用这个对象的属性和方法。 package step1; public class Test { public static void main(String[] args) { /********** Begin **********/ ... -
Java利用 ClassLayout 查看对象头
2021-12-22 10:46:53--查看对象头工具--> <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.9</version> </dependency> ... -
2020-05-03:对象头具体包括什么
2020-06-11 08:22:16福哥口诀法:标类长(对象头:markword标记字、klass类型指针、数组长度(仅限于数组)) 无偏轻重G(锁状态:无锁、偏向锁、轻量级锁、重量级锁、GC标记) 未哈未年标,25 31 1 (64位无锁情况:未使用25、hashcode... -
面向对象高级篇
2013-10-21 21:21:03java面向对象高级篇 super way believe it