4、Java 对象底层

来源:互联网 发布:网络监控显示不在线 编辑:程序博客网 时间:2024/06/15 08:23

Java对象创建

对象创建一共有四种方法(持续更新……)

使用New关键字创建对象

创建过程

使用New关键字创建对象过程

从堆中划分内存的不同情况
堆中划分内存的不同情况

内存分配完成后,虚拟机会将分配到的内存空间都初始化为0(不包括对象头),这保证了对象实例字段在Java代码中不初始化就可以直接使用。
接下来,执行< init>方法,使对象按照程序员的意愿进行初始化。

对象内存布局

在HotSpot虚拟机中,对象在内存中的存储的布局分为3块:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)

对象头

主要包括两部分信息

第一部分主要用于存储对象自身的运行时数据(如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等官方称这些数据为“Mark Word”)这些数据被设计固定的数据结构存储,它会根据对象状态复用自己存储空间。

以32位的HotSpot为例虚拟机对象头

以32位的HotSpot为例虚拟机对象头

对象头的另外一部分是指针类型,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定该对象是哪个类的实例。并不是所有虚拟机实现都必须在对象数据上保留类型指针,即查找对象的元数据信息并不一定要经过对象本身,后续会讨论到(通过句柄访问对象时,句柄池内有对象类型数据指针)

实例数据

这部分的存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在Java源码中定义顺序的影响。HotSpot虚拟机默认的分配策略为longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers),从分配策略中可以看出,相同宽度的字段总是被分配到一起。在满足这个前提条件的情况下,在父类中定义的变量会出现在子类之前。如果CompactFields参数值为true(默认为true),那子类之中较窄的变量也可能会插入到父类变量的空隙之中。

对齐填充

对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是对象的大小必须是8字节的整数倍。对象头部分正好似8字节的倍数(1倍或者2倍),因此当对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。

对象的访问定位

建立对象是为了使用对象,我们的Java程序需要通过栈上的reference数据来操作堆上的具体对象。由于reference类型在Java虚拟机规范里面只规定了是一个指向对象的引用,并没有定义这个引用应该通过什么种方式去定位、访问到堆中的对象的具体位置,对象访问方式也是取决于虚拟机实现而定的。主流的访问方式有使用句柄和直接指针两种。

如果使用句柄访问的话,Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据的具体各自的地址信息。如下图所示。

通过句柄访问对象

如果使用直接指针访问的话,Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,reference中存储的直接就是对象地址,如下图所示。

通过直接指针访问对象

这两种对象访问方式各有优势,使用句柄来访问的最大好处就是reference中存储的是稳定句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要被修改。

使用直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销,由于对象访问的在Java中非常频繁,因此这类开销积小成多也是一项非常可观的执行成本。从上一部分讲解的对象内存布局可以看出,就虚拟机HotSpot而言,它是使用第二种方式进行对象访问,但在整个软件开发的范围来看,各种语言、框架中使用句柄来访问的情况也十分常见。

浅析String.intern()

String.intern( )在不同的JDK版本下可能会有不同运行方式(两者都会先到常量池中寻找,如果有均返回常量池中对象引用,下讨论常量池中无引用时)。

1、JDK1.6中运行时,intern方法会把首次遇到的字符串实例复制到永久代(代码区)去,返回的也是永久代中这个字符串实例的引用,而堆中创建对象地址必然不会等同于该引用地址

JDK1.6中String.intern()方法

2、JDK1.7(及部分其他虚拟机,例如JRockit)的intern( )实现不会再复制实例,只是在常量池中记录首次出现实例的引用,因此intern返回的引用与堆中字符串实例是同一个,但不是首次出现的时候,会返回常量池中存在的那个引用。

JDK1.7中String.intern()方法

阅读全文
1 0
原创粉丝点击