竹笋

首页 » 问答 » 灌水 » JVM系列4一看就懂的对象内存布局
TUhjnbcbe - 2024/8/28 17:33:00

前言

Java中一切皆对象,同时对象也是Java编程中接触最多的概念,深入理解Java对象能够更帮助我们深入地掌握Java技术栈。在这篇文章里,我们将从内存的视角,带你深入理解Java对象在虚拟机中的表现形式。

学习路线图:

1.对象在哪里分配?

在Java虚拟机中,Java堆和方法区是分配对象的主要区域,但是也存在一些特殊情况,例如TLAB、栈上分配、标量替换等。这些特殊情况的存在是虚拟机为了进一步优化对象分配和回收的效率而采用的特殊策略,可以作为知识储备。

1、Java堆(Heap):Java堆是绝大多数对象的分配区域,现代虚拟机会采用分代收集策略,因此Java堆又分为新生代、老生代和永生代。如果新生代使用复制算法,又可以分为Eden区、FromSurvivor区和ToSurvivor区。除了这些每个线程都可以分配对象的区域,如果虚拟机开启了TLAB策略,那么虚拟机会在堆中为每个线程预先分配一小块内存,称为线程本地分配缓冲(ThreadLocalAllocationBuffer,TLAB)。在TLAB上分配对象不需要同步锁定,可以加快对象分配速度(TLAB中的对象依然是线程共享读取的,只是不允许其他线程在该区域分配对象);

2、方法区(MethodArea):方法区也是线程共享的区域,堆中存放的是生命周期较短的对象,而方法区中存放的是生命周期较长的对象,通常是一些支撑虚拟机执行的必要对象,将两种对象分开存储体现的是动静分离的思想,有利于内存管理。存储在方法区中的数据包括已加载的Class对象、静态字段(本质上是Class对象中的实例字段,下文会解释)、常量池(例如String.intern())和即时编译代码等;

3、栈上分配(StackAllocation):如果Java虚拟机通过逃逸分析后判断一个对象的生命周期不会逃逸到方法外,那么可以选择直接在栈上分配对象,而不是在堆上分配。栈上分配的对象会随着栈帧出栈而销毁,不需要经过垃圾收集,能够缓解垃圾收集器的压力。

4、标量替换(ScalarReplacement):在栈上分配策略的基础上,虚拟机还可以选择将对象分解为多个局部变量再进行栈上分配,连对象都不创建。

2.对象的访问定位

Java类型分为基础数据类型(int等)和引用类型(Reference),虽然两者都是数值,但却有本质的区别:基础数据类型本身就代表数据,而引用本身只是一个地址,并不代表对象数据。那么,虚拟机是如何通过引用定位到实际的对象数据呢?具体访问定位方式取决于虚拟机实现,目前有2种主流方式:

1、直接指针访问:引用内部持有一个指向对象数据的直接指针,通过该指针就可以直接访问到对象数据。采用这种方式的话,就需要在对象数据中额外使用一个指针来指向对象类型数据;

2、句柄访问:引用内部持有一个句柄,而句柄内部持有指向对象数据和类型数据的指针(句柄位于Java堆中句柄池)。使用这种方式的话,就不需要在对象数据中记录对象类型数据的指针。

使用句柄的优点是当对象在垃圾收集过程中移动存储区域时,虚拟机只需要改变句柄中的指针,而引用保持稳定。而使用直接指针的优点是只需要一次指针跳转就可以访问对象数据,访问速度相对更快。以SunHotSpot虚拟机而言,采用的是直接指针方式,而AndroidART虚拟机采用的是句柄方式。

handle.h

//AndroidART虚拟机源码体现://HandlesarememorylocationsthatcontainGCroots.Asthemirror::Object*swithinahandleare//GCvisiblethentheGCmaymovethereferenceswithinthem,somethingthatcouldntbedonewith//awrappointer.HandlesaregenerallyallocatedwithinHandleScopes.Handleisasuper-class//ofMutableHandleanddoesntsupportassignmentoperations.templateclassTclassHandle:publicValueObject{  ...}

直接指针访问:

句柄访问:

关于Java引用类型的深入分析,见引用类型

3.使用JOL分析对象内存布局

这一节我们演示使用JOL(JavaObjectLayout)来分析Java对象的内存布局。JOL是OpenJDK提供的对象内存布局分析工具,不过它只支持HotSpot/OpenJDK虚拟机,在其他虚拟机上使用会报错:

错误日志

java.lang.IllegalStateException:OnlyHotSpot/OpenJDKVMsaresupported

3.1使用步骤

现在,我们使用JOL分析newObject()在HotSpot虚拟机上的内存布局,模板程序如下:

示例程序

//步骤一:添加依赖implementationorg.openjdk.jol:jol-core:0.11//步骤二:创建对象Objectobj=newObject();//步骤三:打印对象内存布局//1.输出虚拟机与对象内存布局相关的信息System.out.println(VM.current().details());//2.输出对象内存布局信息System.out.println(ClassLayout.parseInstance(obj).toPrintable());

输出日志

#Running64-bitHotSpotVM.#Using

1
查看完整版本: JVM系列4一看就懂的对象内存布局