一个String对象到底多大?

一个String对象到底多大?

参考:一个java对象到底占用多大内存

JAVA对象的组成部分

对象头 + 实例数据 + 对齐填充

HotSpot的对齐方式为8字节对齐:

(对象头 + 实例数据 + padding) % 8等于0且0 <= padding < 8

每个组成部分的大小

32位和64位系统上对象头的大小是不同的,开启指针压缩后,各部分大小也会有所不同。

单位:byte

64位指针压缩(-XX:+UseCompressedOops)
对象头1612
实例数据为引用类型84
数组对象头2416

计算对象大小

对象本身大小 = 当前类及超类的基本类型实例字段大小 + 引用类型实例字段引用大小 + 实例基本类型数组总占用空间 + 实例引用类型数组引用本身占用空间大小;

手动计算后,校验是否正确

  • 使用JOL工具
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>
public class SizeOfObjectDemo2 {

	/**
	 * 64位虚拟机上开启指针压缩后,String对象占用内存总大小为24 + 16 + 2 * 6 + 4 =24 + 32 =56
	 * jdk 8
	 * @param args
	 */
	public static void main(String[] args) {
		String s = "123678";
		// 启用压缩后: 16(对象头) + 2 * 3 + 2 = 24
		char[] c = new char[]{'1','2','3'};
        // 对象本身大小
		print(ClassLayout.parseInstance(s).toPrintable());
		// 获取对象总大小
		print("size : " + GraphLayout.parseInstance(s).totalSize());
	}

	static void print(String message) {
		System.out.println(message);
		System.out.println("-------------------------");
	}
}

string 对象本身大小
java.lang.String object internals:

OFFSETSIZETYPE DESCRIPTIONVALUE
04(object header)01 00 00 00 (00000001 00000000 00000000 00000000) (1)
44(object header)00 00 00 00 (00000000 00000000 00000000 00000000) (0)
84(object header)da 02 00 f8 (11011010 00000010 00000000 11111000) (-134216998)
124char[] String.value[1, 2, 3, 6, 7, 8]
164int String.hash0
204(loss due to the next object alignment)
Instance size: 24 bytes

而一个对象的总大小需要递归计算包含的对象的大小。比如String对象计算它的总大小的时候就需要计算char数组的大小,这里计算了char[] c = new char[]{‘1’,‘2’,‘3’,‘4’,‘5’,‘6’};
[C object internals:

OFFSETSIZETYPE DESCRIPTIONVALUE
04(object header)01 00 00 00 (00000001 00000000 00000000 00000000) (1)
44(object header)00 00 00 00 (00000000 00000000 00000000 00000000) (0)
84(object header)41 00 00 f8 (01000001 00000000 00000000 11111000) (-134217663)
124(object header)06 00 00 00 (00000110 00000000 00000000 00000000) (6)
1612char [C.N/A
284(loss due to the next object alignment)
Instance size: 32 bytes
  • 使用Instrumentation
public class SizeOfObject {
	static Instrumentation inst;

	public static void premain(String args, Instrumentation instP) {
		inst = instP;
	}

	/**
	 * 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、<br></br>
	 * 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;<br></br>
	 * 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 <br></br>
	 *
	 * @param obj
	 * @return
	 */
	public static long sizeOf(Object obj) {
		return inst.getObjectSize(obj);
	}

	/**
	 * 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小
	 *
	 * @param objP
	 * @return
	 * @throws IllegalAccessException
	 */
	public static long fullSizeOf(Object objP) throws IllegalAccessException {
		Set<Object> visited = new HashSet<Object>();
		Deque<Object> toBeQueue = new ArrayDeque<>();
		toBeQueue.add(objP);
		long size = 0L;
		while (toBeQueue.size() > 0) {
			Object obj = toBeQueue.poll();
			// sizeOf的时候已经计基本类型和引用的长度,包括数组
			size += skipObject(visited, obj) ? 0L : sizeOf(obj);
			Class<?> tmpObjClass = obj.getClass();
			if (tmpObjClass.isArray()) {
				//[I , [F 基本类型名字长度是2
				if (tmpObjClass.getName().length() > 2) {
					for (int i = 0, len = Array.getLength(obj); i < len; i++) {
						Object tmp = Array.get(obj, i);
						if (tmp != null) {
							//非基本类型需要深度遍历其对象
							toBeQueue.add(Array.get(obj, i));
						}
					}
				}
			} else {
				while (tmpObjClass != null) {
					Field[] fields = tmpObjClass.getDeclaredFields();
					for (Field field : fields) {
						//静态不计   基本类型不重复计
						if (Modifier.isStatic(field.getModifiers())
								|| field.getType().isPrimitive()) {
							continue;
						}

						field.setAccessible(true);
						Object fieldValue = field.get(obj);
						if (fieldValue == null) {
							continue;
						}
						toBeQueue.add(fieldValue);
					}
					tmpObjClass = tmpObjClass.getSuperclass();
				}
			}
		}
		return size;
	}

	/**
	 * String.intern的对象不计;计算过的不计,也避免死循环
	 *
	 * @param visited
	 * @param obj
	 * @return
	 */
	static boolean skipObject(Set<Object> visited, Object obj) {
		if (obj instanceof String && obj == ((String) obj).intern()) {
			return true;
		}
		return visited.contains(obj);
	}
}
<build>
   <plugins>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-assembly-plugin</artifactId>
         <configuration>
            <descriptorRefs>
               <descriptorRef>jar-with-dependencies</descriptorRef>
            </descriptorRefs>
            <archive>
               <manifestEntries>
                  <Premain-Class>com.example.instrumentation.SizeOfObject</Premain-Class>
                  <!--<Agent-Class>com.example.instrumentation.MyJavaAgent</Agent-Class>
                  <Can-Redefine-Classes>true</Can-Redefine-Classes>
                  <Can-Retransform-Classes>true</Can-Retransform-Classes>-->
               </manifestEntries>
            </archive>
         </configuration>

         <executions>
            <execution>
               <goals>
                  <goal>attached</goal>
               </goals>
               <phase>package</phase>
            </execution>
         </executions>
      </plugin>
   </plugins>
</build>

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值