java对象的内存布局(一):计算java对象占用的内存空间以及java object layout工具的使用

最近在学习java对象内存布局方面的一些知识,主要是想知道一个java对象到底占用多少内存空间,以及java对象在内存中到底是什么样子的。c/c++中的sizeof运算符能够方便地告诉我们一个变量占用的内存空间,但是在java中却没有直接提供这种机制。如果想获取java对象占用的内存大小,可以利用java的Instrumentation机制。java.lang.instrument.Instrumentation这个接口提供了getObjectSize(Object
objectToSize),能够告诉我们一个对象的大小。如何获取Instrumentation对象,网上有很多资料,这里不再赘述。

public static void main(String[] args)
{
	// 通过pre-agent获取Instrumentation对象
	Instrumentation instr = AgentGetter.getInstrumentation();

	System.out.println(instr.getObjectSize(new Object()));
}

Java对象的内存布局:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。无论是32位还是64位的HotSpot,使用的都是8字节对齐。也就是说每个java对象,占用的字节数都是8的整数倍。(对象头 + 实例数据 + padding) % 8等于0且0 <= padding < 8。在网上看到各种介绍如何手动计算对象大小的文章,总结了几点:

1.基本数据类型占用的字节数,JVM规范中有明确的规定,无论是在32位还是64位的虚拟机,占用的内存大小是相同的。

2.reference类型在32位JVM下占用4个字节,但是在64位下可能占用4个字节或8个字节,这取决于是否启用了64位JVM的指针压缩参数UseCompressedOops。

3.new Object()这个对象在32位JVM上占8个字节,在64位JVM上占16个字节。

4.开启(-XX:+UseCompressedOops)指针压缩,对象头占12字节; 关闭(-XX:-UseCompressedOops)指针压缩,对象头占16字节。

5.64位JVM上,数组对象的对象头占用24个字节,启用压缩之后占用16个字节。之所以比普通对象占用内存多是因为需要额外的空间存储数组的长度。

6.对象内存布局中的实例数据,不包括类的static字段的大小,因为static字段是属于类的,被该类的所有对象共享。

更具体的可以参考“一个Java对象到底占用多大内存?”这篇文章。在网上搜索的过程中看到了java object layout这个小工具,能够打印出类的布局信息。

项目的主页是:http://openjdk.java.net/projects/code-tools/jol/

项目代码在http://central.maven.org/maven2/org/openjdk/jol/

下载jol-core-0.3.2.jar之后,将其加入项目的build path,通过下面的代码就可以查看某个类的布局信息。

package jol;

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;

public class T1 {

	public static void main(String[] args) throws Exception {

		System.out.println(VMSupport.vmDetails());

        System.out.println(ClassLayout.parseClass(VO.class).toPrintable());
	}

}

运行这段代码,可以看到jol输出如下信息:

Running 64-bit HotSpot VM.
Objects are 8 bytes aligned.
Field sizes by type: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 8, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

jol.VO object internals:
 OFFSET  SIZE  TYPE DESCRIPTION                    VALUE
      0    16       (object header)                N/A
     16     8  long VO.b                           N/A
     24     4   int VO.a                           N/A
     28     4       (loss due to the next object alignment)
Instance size: 32 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

可以看到jol工具,能够显示出对象头的大小,以及每个实例字段的偏移,进而计算对象占用的内存大小。

jol工具还提供了一个命令行工具jol-cli-0.3.2-full.jar,包含了main方法能够直接在命令行运行,查看类的布局信息。可以从这里下载jol-cli工具。

#查看jdk系统类的布局信息
>java -jar jol-cli-0.3.2-full.jar internals java.lang.Object
#如果我们自己的jar放在C:\Program Files\Java\jdk1.7.0_80\jre\lib\ext\下,那么会自动加载;
#如果我们自己的jar放在C:\Program Files\Java\jdk1.7.0_80\jre\lib\下,那么不会自动加载(可能是因为安全问题#,bootstrap类加载器仅仅加载jdk的核心类);
>java -jar jol-cli-0.3.2-full.jar internals net.aty.VO
#方式1:指定classpath,查看自己的类布局
>java -jar jol-cli-0.3.2-full.jar internals -cp mydemo.jar net.aty.VO
#方式2:指定classpath,查看自己的类布局
>java -cp mydemo.jar;jol-cli-0.3.2-full.jar org.openjdk.jol.Main internals net.aty.VO

上面几种方式都能够正确运行,但是下面这种方式是错误的,会报错ClassNotFoundException。

#注意:这种方式会报错:java.lang.ClassNotFoundException:
#这是因为:如果采用了-jar参数来运行可执行的jar包,JVM会忽略你设置的classpath以及所有环境变量
java -cp mydemo.jar -jar jol-cli-0.3.2-full.jar internals net.aty.VO
时间: 2024-10-14 06:00:22

java对象的内存布局(一):计算java对象占用的内存空间以及java object layout工具的使用的相关文章

【深入理解JVM】:Java对象的创建、内存布局、访问定位

对象的创建 一个简单的创建对象语句Clazz instance = new Clazz();包含的主要过程包括了类加载检查.对象分配内存.并发处理.内存空间初始化.对象设置.执行ini方法等. 主要流程如下: 1. 类加载检查 JVM遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初始化过.如果没有,那必须先执行相应的类的加载过程. 2. 对象分配内存 对象所需内存的大小在类加载完成后便完全确定(对象内存布局),

JVM 对象的创建、内存布局

一.对象的创建过程 一个简单的创建对象语句Clazz instance = new Clazz();包含的主要过程包括了类加载检查.对象分配内存.并发处理.内存空间初始化.对象设置.执行init方法等. 主要流程如下: 1. 类加载检查 JVM遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初始化过.如果没有,那必须先执行相应的类的加载过程. 2. 对象分配内存 对象所需内存的大小在类加载完成后便完全确定(对象内

使用sos查看.NET对象内存布局

前面我们图解了.NET里各种对象的内存布局,我们再来从调试器和clr源码的角度来看一下对象的内存布局.我写了一个测试程序来加深对.net对象内存布局的了解: using System; using System.Runtime.InteropServices; // 实际上是一个C语言里的联合体 [StructLayout(LayoutKind.Explicit)] public struct InnerStruct { [FieldOffset(0)] public float FloatVa

c++对象内存模型【内存布局】(转)

总结:1.按1继承顺序先排布基于每个父类结构.2.该结构包括:基于该父类的虚表.该父类的虚基类表.父类的父类的成员变量.父类的成员变量.3.多重继承且连续继承时,虚函数表按继承顺序排布函数与虚函数.4.而后排布子类的成员变量.5.排布虚基类的虚函数表.6.虚基类的成员变量 #类中的元素 0. 成员变量   1. 成员函数   2. 静态成员变量   3. 静态成员函数   4. 虚函数   5. 纯虚函数 #影响对象大小的因素 0. 成员变量     1. 虚函数表指针(_vftptr)   2

【转】c++继承中的内存布局

今天在网上看到了一篇写得非常好的文章,是有关c++类继承内存布局的.看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用. ——谈VC++对象模型(美)简.格雷程化    译 译者前言 一个C++程序员,想要进一步提升技术水平的话,应该多了解一些语言的语意细节.对于使用VC++的程序员来说,还应该了解一些VC++对于C++的诠释. Inside the C++ Object Model虽然是一本好书,然而,书的篇幅多一些,又和具体的VC++关系小一些.因此,从篇幅和内容来看,译者认为本文

UNIX高级环境编程(8)进程环境(Process Environment)- 进程的启动和退出、内存布局、环境变量列表

在学习进程控制相关知识之前,我们需要了解一个单进程的运行环境. 本章我们将了解一下的内容: 程序运行时,main函数是如何被调用的: 命令行参数是如何被传入到程序中的: 一个典型的内存布局是怎样的: 如何分配内存: 程序如何使用环境变量: 程序终止的各种方式: 跳转(longjmp和setjmp)函数的工作方式,以及如何和栈交互: 进程的资源限制 ? 1 main函数 main函数声明: int main (int argc, char *argv[]); 参数说明: argc:命令行参数个数

一个由进程内存布局异常引起的问题

一个由进程内存布局异常引起的问题 前段时间业务反映某类服务器上更新了 bash 之后,ssh 连上去偶发登陆失败,客户端吐出错误信息如下所示:图 - 0 该版本 bash 为部门这边所定制,但实现上并没有改动原有逻辑,只是加入了些监控功能,那么这些错误从哪里来呢? 是 bash 的锅吗 从上面的错误信息可以猜测,异常是 bash 在启动过程中分配内存失败所导致,看起来像是某些情况下该进程错误地进行了大量内存分配,最后导致内存不足,要确认这个事情比较简单,动态内存分配到系统调用这一层上主要就两种方

Java对象的内存布局以及对象所需内存大小计算详解

1. 内存布局 在HotSpot虚拟机中,对象的内存布局可以分为三部分:对象头(Header). 实例数据(Instance Data)和对齐填充(Padding). 1) 对象头(Header): 对象头又可以分为两部分: 第一部分用来存储对象自身的运行时基本数据信息.如哈希码.GC分代年龄.锁状态标示.线程持有的锁等.这部分数据的长度在32bit和64bit虚拟机上分别为32bit和64bit.官方称呼是"Mark Word". 第二部分是类型指针,或者说指向类的元数据信息的引用.

Java对象的内存布局

Java对象的内存布局:对象头(Header),实例数据(Instance Data),对齐填充(Padding):另外:不同的环境结果可能有差异,我所在的环境是HotSpot虚拟机,64位Windows. 对象头 对象头在32位系统上占用8bytes,64位系统上占用16bytes. System.out.println("sizeOf(new Object()) = " + sizeOf(new Object())); sizeOf(new Object()) = 16 实例数据