searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

对象的内存布局

2024-07-29 09:58:53
17
0

1.对象的创建过程

对象的创建过程,比如我们要创建一个对象new T();第一步就是要把class loading到内存,第二步就是linking的过程,其中linking包括三步:1.verification校验 2.preparation把类的静态变量设默认值 3.resolution做一个解析;第三步就是initializing的过程,把类的静态变量设为初始值同时执行静态语句块。第四步就是申请内存;第五步就是成员变量再赋默认值;第六步,调用构造方法语句super调用父类。

2.对象布局

对象的内存布局分为两种,第一种是普通对象,第二种是数组对象。
普通对象的内存布局有:1.对象头,在hotport里面称为markword长度是8字节;2.classPointer指针:-XX:+UseCompressedClassPointers为4字节,不开启为8字节;3.实例数据,引用类型:-XX:+UseCompressedOops为4字节,不开启为8字节;4.Padding对齐,这个对齐是8的倍数。
数组对象的内存布局有:1.对象头,在hotport里面称为markword长度是8字节;2.classPointer指针:-XX:+UseCompressedClassPointers为4字节,不开启为8字节;3.数组长度:4字节;4.数组数据;5.Padding对齐。

3.对象头具体包括什么

1.类型信息:类型描述符指针:对象头的第一个字段通常是类型信息,这通常是一个指向类型描述符的指针。该描述符包含了关于对象类型的更多信息,如对象的大小、布局等。这有助于虚拟机或运行时环境识别对象的类型,从而进行正确的操作。
2.运行时数据:MarkWord(标记字):在Java中,对象头包含一个名为Mark Word的字段,用于存储对象自身的运行时数据。这些数据包括但不限于GC(垃圾收集)标志位、哈希码、锁状态等。GC标志位用于标记对象是否可被垃圾收集;哈希码用于支持基于哈希的集合操作;锁状态则与对象的同步机制相关。
3.指向类元数据的指针:ClassPointer(类对象指针):对象头还包含一个指向类元数据的指针(在Java中称为Class Pointer)。这个指针指向对象的类在方法区的元数据,虚拟机通过这个指针来确定这个对象是哪个类的实例。类元数据包含了类的结构信息,如方法、字段等。
4.数组长度(可选):如果对象是数组类型,对象头还会包含一个数组长度的字段。这个字段记录了数组中元素的数量,有助于运行时环境对数组进行索引和遍历。
5.其它元数据:对象头还可能包含其他元数据,如对象的创建时间、生命周期状态等。这些信息对于调试、监控和性能优化等场景非常有用。

4.对象的定位

当我们创建一个对象时T t = new T();这个小t是怎么找到这个对象的,有两种方式。
第一种方式:句柄池。在这种方式中,Java堆中会维护一个句柄池(或称为句柄表),每个句柄都包含两个指针:一个指向对象的实际数据,另一个指向对象的类型信息(即Class对象)。局部变量(如T t)会存储一个句柄的引用,而不是直接引用对象本身。这种方式的好处是,在对象被移动(比如垃圾收集过程中的内存整理)时,只需要修改句柄表中的指针,而不需要修改所有指向该对象的引用,从而提高了效率。然而,这种方式增加了间接性,每次访问对象都需要先通过句柄,这可能稍微降低了一些访问速度。
第二种方式:直接指针。在这种方式中,局部变量(如T t)直接存储了对象的内存地址。这种方式的优点是访问速度快,因为可以直接通过地址访问对象。但是,当对象移动时(虽然在现代垃圾收集器中较为罕见),所有指向该对象的引用都需要更新,这可能会增加垃圾收集器的复杂性。
HotSpot VM的实现:实际上,Java HotSpot VM采用的是类似第二种方式但有所优化的策略。它直接存储对象的引用,但在内部使用了一种称为“写屏障”(Write Barrier)的技术来处理对象移动的问题。当对象在堆上移动时,写屏障会确保所有指向该对象的引用都被更新。
优化讨论:1.减少不必要的对象创建;2.避免过深的对象图;3.注意引用类型;使用合适的垃圾搜集器。

5.对象分配过程

1.对象创建:当使用new关键字创建一个对象时,JVM会在堆内存中为对象分配空间。堆内存是JVM管理的一块用于存放对象实例的内存区域。
2.线程本地分配:为了提升对象分配的效率,JVM的某些实现(如HotSpot)会采用线程本地分配缓冲区(TLAB)。TLAB是一个线程专用的内存区域,用于快速分配对象。当线程需要创建对象时,会首先尝试在TLAB中分配空间。如果TLAB足够大且还有剩余空间,则对象会直接在TLAB中分配,这样可以减少线程间的同步开销。如果TLAB空间不足或不存在,JVM会尝试在堆内存中直接分配空间。
3.堆内存分配:如果对象不能在TLAB中分配,JVM会在堆内存中寻找足够的连续空间来存放新对象。堆内存通常被分为新生代(Young Generation)和老年代(Old Generation)。新生代又进一步细分为伊甸区(Eden Space)、两个幸存者区(Survivor Spaces,通常称为From和To或S0和S1)。
4.对象在新生代的分配:新创建的对象首先会被分配到新生代的伊甸区。随着对象的创建和销毁,伊甸区会逐渐填满。当伊甸区空间不足时,会触发一次Minor GC(也称为Young GC),该过程会清理掉伊甸区中不再被引用的对象,并将存活的对象移动到幸存者区中的一个(通常是To区)。在多次Minor GC后,如果对象仍然存活,并且达到了晋升到老年代的条件(如年龄阈值),则对象会被移动到老年代。
5.对象在老年代的分配:大对象(需要连续大量内存空间的对象)和长期存活的对象(经过多次Minor GC后仍然存活的对象)会直接被分配到老年代。

0条评论
作者已关闭评论
骆****鹏
2文章数
0粉丝数
骆****鹏
2 文章 | 0 粉丝
骆****鹏
2文章数
0粉丝数
骆****鹏
2 文章 | 0 粉丝
原创

对象的内存布局

2024-07-29 09:58:53
17
0

1.对象的创建过程

对象的创建过程,比如我们要创建一个对象new T();第一步就是要把class loading到内存,第二步就是linking的过程,其中linking包括三步:1.verification校验 2.preparation把类的静态变量设默认值 3.resolution做一个解析;第三步就是initializing的过程,把类的静态变量设为初始值同时执行静态语句块。第四步就是申请内存;第五步就是成员变量再赋默认值;第六步,调用构造方法语句super调用父类。

2.对象布局

对象的内存布局分为两种,第一种是普通对象,第二种是数组对象。
普通对象的内存布局有:1.对象头,在hotport里面称为markword长度是8字节;2.classPointer指针:-XX:+UseCompressedClassPointers为4字节,不开启为8字节;3.实例数据,引用类型:-XX:+UseCompressedOops为4字节,不开启为8字节;4.Padding对齐,这个对齐是8的倍数。
数组对象的内存布局有:1.对象头,在hotport里面称为markword长度是8字节;2.classPointer指针:-XX:+UseCompressedClassPointers为4字节,不开启为8字节;3.数组长度:4字节;4.数组数据;5.Padding对齐。

3.对象头具体包括什么

1.类型信息:类型描述符指针:对象头的第一个字段通常是类型信息,这通常是一个指向类型描述符的指针。该描述符包含了关于对象类型的更多信息,如对象的大小、布局等。这有助于虚拟机或运行时环境识别对象的类型,从而进行正确的操作。
2.运行时数据:MarkWord(标记字):在Java中,对象头包含一个名为Mark Word的字段,用于存储对象自身的运行时数据。这些数据包括但不限于GC(垃圾收集)标志位、哈希码、锁状态等。GC标志位用于标记对象是否可被垃圾收集;哈希码用于支持基于哈希的集合操作;锁状态则与对象的同步机制相关。
3.指向类元数据的指针:ClassPointer(类对象指针):对象头还包含一个指向类元数据的指针(在Java中称为Class Pointer)。这个指针指向对象的类在方法区的元数据,虚拟机通过这个指针来确定这个对象是哪个类的实例。类元数据包含了类的结构信息,如方法、字段等。
4.数组长度(可选):如果对象是数组类型,对象头还会包含一个数组长度的字段。这个字段记录了数组中元素的数量,有助于运行时环境对数组进行索引和遍历。
5.其它元数据:对象头还可能包含其他元数据,如对象的创建时间、生命周期状态等。这些信息对于调试、监控和性能优化等场景非常有用。

4.对象的定位

当我们创建一个对象时T t = new T();这个小t是怎么找到这个对象的,有两种方式。
第一种方式:句柄池。在这种方式中,Java堆中会维护一个句柄池(或称为句柄表),每个句柄都包含两个指针:一个指向对象的实际数据,另一个指向对象的类型信息(即Class对象)。局部变量(如T t)会存储一个句柄的引用,而不是直接引用对象本身。这种方式的好处是,在对象被移动(比如垃圾收集过程中的内存整理)时,只需要修改句柄表中的指针,而不需要修改所有指向该对象的引用,从而提高了效率。然而,这种方式增加了间接性,每次访问对象都需要先通过句柄,这可能稍微降低了一些访问速度。
第二种方式:直接指针。在这种方式中,局部变量(如T t)直接存储了对象的内存地址。这种方式的优点是访问速度快,因为可以直接通过地址访问对象。但是,当对象移动时(虽然在现代垃圾收集器中较为罕见),所有指向该对象的引用都需要更新,这可能会增加垃圾收集器的复杂性。
HotSpot VM的实现:实际上,Java HotSpot VM采用的是类似第二种方式但有所优化的策略。它直接存储对象的引用,但在内部使用了一种称为“写屏障”(Write Barrier)的技术来处理对象移动的问题。当对象在堆上移动时,写屏障会确保所有指向该对象的引用都被更新。
优化讨论:1.减少不必要的对象创建;2.避免过深的对象图;3.注意引用类型;使用合适的垃圾搜集器。

5.对象分配过程

1.对象创建:当使用new关键字创建一个对象时,JVM会在堆内存中为对象分配空间。堆内存是JVM管理的一块用于存放对象实例的内存区域。
2.线程本地分配:为了提升对象分配的效率,JVM的某些实现(如HotSpot)会采用线程本地分配缓冲区(TLAB)。TLAB是一个线程专用的内存区域,用于快速分配对象。当线程需要创建对象时,会首先尝试在TLAB中分配空间。如果TLAB足够大且还有剩余空间,则对象会直接在TLAB中分配,这样可以减少线程间的同步开销。如果TLAB空间不足或不存在,JVM会尝试在堆内存中直接分配空间。
3.堆内存分配:如果对象不能在TLAB中分配,JVM会在堆内存中寻找足够的连续空间来存放新对象。堆内存通常被分为新生代(Young Generation)和老年代(Old Generation)。新生代又进一步细分为伊甸区(Eden Space)、两个幸存者区(Survivor Spaces,通常称为From和To或S0和S1)。
4.对象在新生代的分配:新创建的对象首先会被分配到新生代的伊甸区。随着对象的创建和销毁,伊甸区会逐渐填满。当伊甸区空间不足时,会触发一次Minor GC(也称为Young GC),该过程会清理掉伊甸区中不再被引用的对象,并将存活的对象移动到幸存者区中的一个(通常是To区)。在多次Minor GC后,如果对象仍然存活,并且达到了晋升到老年代的条件(如年龄阈值),则对象会被移动到老年代。
5.对象在老年代的分配:大对象(需要连续大量内存空间的对象)和长期存活的对象(经过多次Minor GC后仍然存活的对象)会直接被分配到老年代。

文章来自个人专栏
文章 | 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0