Java对象

对象创建过程

以简单对象的创建为例,说明对象创建过程,新建ObjectTest.java,代码如下:

public class ObjectTest {
    public static void main(String[] args) {
        Person person = new Person("Json");
        System.out.println(person.getName());
    }

    static class Person {
        private String mName;

        public Person(String mName) {
            this.mName = mName;
        }

        public String getName() {
            return mName;
        }

        public void setName(String mName) {
            this.mName = mName;
        }
    }
}

随后执行javac ObjectTest.java命令,编译该文件,生成ObjectTest.class,使用javap -c ObjectTest.class查看字节码,代码如下:

ObjectTest文件字节码1

从字节码可以看出Person对象创建时,首先在code 0位置调用new关键词创建Person对象,随后在code 6位置使用invokespecial调用Person类的init函数,随后代码中就开始进行Person对象的属性获取,调用该对象的相关函数了,从这里来看一个Java对象创建应该分为两步:new指令执行init函数

那么问题来了,new指令到底干了什么,为什么执行玩就创建了一个对象,从代码中我们可以看到并没有声明init函数,那么这个函数又是从哪儿来的?

new指令

对于Java虚拟机而言,new指令意味着创建普通对象,该指令接受一个操作数,指向常量池中的一个引用,该引用用于指示要创建的对象类型,以前文代码为例,#2位置为new指令接受参数,其指向的是Person这个类,如下图

1.1.2.2

那么虚拟机中又是怎么响应new指令的呢?

在虚拟机中,处理new指令一般分为以下几步:

  1. 校验:检查类的符号引用,查看对象的类模版是否已经被加载(如果未加载会执行加载机制),校验字节码文件正确性。
  2. 分配内存:类加载校验完成后,类对象的相关信息已确认,此时就可以根据创建了类对象所需的大小来进行内存分配。
  3. 初始化:针对分配的内存进行初始化工作,包括静态变量赋初值并执行静态代码等。
  4. 设置:设置对象头,标记对象实例,Hash值,锁状态等信息,完成MarkWord和kClassPointer填充。

init函数

在new指令执行完成后,也就意味着在内存中我们拥有了一个类的实例对象,到此就会执行我们编写的构造函数,根据开发人员的设计进行初始化,使得对象的状态符合开发人员预期,init函数执行完成后,一个Java对象的创建也就完成了。

对象组成

在Hotspot虚拟机中,Java对象由对象头,实例数据,对齐填充三部分组成

  • 对象头(Object Header):包含了关于堆对象布局,类型,GC状态,同步状态和标识哈希码等基本信息
  • 实例数据(Instance Data):主要存放类的数据信息,包括父类信息,对象字段属性等
  • 对齐填充(Padding):为了字节对齐,填充的数据,非必须

对象头

HotSpot虚拟机官方文档中可以看到是这样描述对象头的:

object header

Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object's layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format.

大概含义是对象头是受GC管理的每一个对象的开头部分,其结构是通用的(每个oop都指向一个对象头)。对象头中包含有关堆对象布局,类型,GC状态,同步状态和身份哈希码的基本信息,其由两部分组成,在数组中,紧随其后的是一个长度字段。需要注意的是Java对象和VM内部对象都具有共同的对象头。

继续查找官方文档,我们可以看到对象头的两个部分分别为 klass pointer(类指针)mark word(标记词)

mark word

HotSpot虚拟机官方文档中这样描述Mark Word:

mark word

The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.

大概含义是 Mark Word是每个对象对象头的第一部分。通常是一组位域,包含同步状态和身份哈希码。也可以是指向同步相关信息的指针(具有特征低位编码)。在GC执行期间,有可能包含GC状态。

Mark Word在32位JVM中的长度是32位,在64位JVM中长度是64位。Mark Word在不同的锁状态下存储的内容不同,32位存储内容如下图:

32_markword

64位存储内容如下:

64_markword

从上面两张图可以看出,对于32或64位JVM中的Mark Word而言,虽然其数据占位长度有所差异,但其中组成内容基本是一致的:

  • 锁标志位(lock):区分锁状态
  • biased_lock:是否偏向锁
  • 分代年龄:对象被GC的次数,次数到达阀值,对象就会被转移到老年代
  • 对象的hashcode:运行期间调用System.identityHashCode()来计算,延迟计算,并把结果赋值到这里。当对象加锁后,计算的结果31位不够表示,在偏向锁,轻量锁,重量锁,hashcode会被转移到Monitor中
  • 偏向锁的线程id:偏向模式的时候,当某个线程持有对象的时候,对象这里就会被置为该线程的ID。 在后面的操作中,就无需再进行尝试获取锁的动作
  • epoch:偏向锁在CAS锁操作过程中,偏向性标识,表示对象更偏向哪个锁
  • ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针。当锁获取是无竞争的时,JVM使用原子操作而不是OS互斥。这种技术称为轻量级锁定。在轻量级锁定的情况下,JVM通过CAS操作在对象的标题字中设置指向锁记录的指针
  • ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针。如果两个不同的线程同时在同一个对象上竞争,则必须将轻量级锁定升级到Monitor以管理等待的线程。在重量级锁定的情况下,JVM在对象的ptr_to_heavyweight_monitor设置指向Monitor的指针

kclass pointer

HotSpot虚拟机官方文档中这样描述kclass pointer:

klass pointer

The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the "klass" contains a C++ style "vtable".

大概含义是类指针是kclass pointer是每个对象对象头的第二部分。指向描述原始对象布局和行为的另一个对象(元对象)。对于Java对象而言,kclass中包含C++样式的“vtable”。

虚拟机正是通过这个指针来确定这个对象是那个类的实例。

对象引用方式

我们都知道对象在构造后,可以传递给其他对象,当然也可以在其他对象内构造持有该对象,此时我们可以称该对象被其他对象引用,因被其他对象引用后发生GC(见对象管理中的说明)时,是否可以回收该对象,又可以把引用分为强引用,弱引用,软引用,虚引用四种引用方式,各引用方式与是否可被GC回收如下图所示:

引用类型 GC时是否可回收 备注
强引用 不可回收 无论何时,只要强引用关系存在,则该对象不会被垃圾收集器回收
软引用 内存溢出前,选中软引用对象做二次回收 当软引用对象回收后,如果内存仍然不足,则会继续跑出内存溢出异常
弱引用 下次垃圾收集器工作时被回收 弱引用对象只能存活到下次垃圾收集器工作之前
虚引用 最弱的引用方式,虚引用完全不会对对象的生存时间造成影响 为对象设置虚引用关联的唯一目的是在这个对象被垃圾收集器回收时收到一个通知
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容