在开发过程中,我们有时需要来获取某个对象的大小,以方便我们参考,来决定开发的技术方案。jvm中提供了两种方式来获取一个对象的大小。

通过Instrumentation来计算对象的大小

  • 编写计算代码:
package com.java.basic;import java.lang.instrument.Instrumentation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Stack;public class SizeOfAgent
{  private static Instrumentation inst;  /** initializes agent */  public static void premain(String agentArgs, Instrumentation instP)   {  inst = instP;  }  /** * Returns object size without member sub-objects. * @param o object to get size of * @return object size */  public static long sizeOf(Object o)   {  if(inst == null)   {  throw new IllegalStateException("Can not access instrumentation environment.\n" +  "Please check if jar file containing SizeOfAgent class is \n" +  "specified in the java's \"-javaagent\" command line argument.");  }  return inst.getObjectSize(o);  }  /** * Calculates full size of object iterating over * its hierarchy graph. * @param obj object to calculate size of * @return object size */  public static long fullSizeOf(Object obj)   {  Map<Object, Object> visited = new IdentityHashMap<Object, Object>();  Stack<Object> stack = new Stack<Object>();  long result = internalSizeOf(obj, stack, visited);  while (!stack.isEmpty())   {  result += internalSizeOf(stack.pop(), stack, visited);  }  visited.clear();  return result;  }                 private static boolean skipObject(Object obj, Map<Object, Object> visited)   {  if (obj instanceof String) {//这个if是bug,应当去掉--teasp  // skip interned string  if (obj == ((String) obj).intern()) {  return true;  }  }  return (obj == null) || visited.containsKey(obj);  }  @SuppressWarnings("rawtypes")  private static long internalSizeOf(Object obj, Stack<Object> stack, Map<Object, Object> visited)   {  if (skipObject(obj, visited))  {  return 0;  }  visited.put(obj, null);  long result = 0;  // get size of object + primitive variables + member pointers   result += SizeOfAgent.sizeOf(obj);  // process all array elements  Class clazz = obj.getClass();  if (clazz.isArray())   {  if(clazz.getName().length() != 2)   {// skip primitive type array  int length =  Array.getLength(obj);  for (int i = 0; i < length; i++)   {  stack.add(Array.get(obj, i));  }   }         return result;  }  // process all fields of the object  while (clazz != null)   {  Field[] fields = clazz.getDeclaredFields();  for (int i = 0; i < fields.length; i++)   {  if (!Modifier.isStatic(fields[i].getModifiers()))   {  if (fields[i].getType().isPrimitive())   {  continue; // skip primitive fields  }   else   {  fields[i].setAccessible(true);  try   {  // objects to be estimated are put to stack  Object objectToAdd = fields[i].get(obj);  if (objectToAdd != null)   {                          stack.add(objectToAdd);  }  }   catch (IllegalAccessException ex)   {   assert false;   }  }  }  }  clazz = clazz.getSuperclass();  }  return result;  }
}  

其中sizeof方法仅仅获取的是当前对象的大小,而该对象的如果存在对其他对象的引用,则不在计算范围以内,而fullsizeof则会计算整体的大小。

  • 将该java文件进行编译,并打成jar包
  1. com.java.basic.SizeOfAgent .java

jar cvf sizeOfAgent.jar com/java.basic/SizeOfAgent .class

  • 修改META-INF/MANIFEST.MF文件内容
    Premain-Class: com.java.basic.SizeOfAgent

Boot-Class-Path:
Can-Redefine-Classes: false
注意:每个冒号后面都有一个空格,且最后一行会有一个换行

  • 将该jar包导入项目
  • 添加启动参数:-javaagent:E:sizeOfAgent.jar
    我这边是将该jar包放在e盘,这里填写绝对路径。

这样我们就可以通过调用该类中的sizeOf方法或者fullSizeOf方法即可。

使用Unsafe类来获取对象大小

unsafe对象可以获取到一个对象中各个属性的内存指针的偏移量,可以利用其来计算一个对象的大小。

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;import sun.misc.Unsafe;public class ClassIntrospector {private static final Unsafe unsafe;/** Size of any Object reference */private static final int objectRefSize;static {try {Field field = Unsafe.class.getDeclaredField("theUnsafe");field.setAccessible(true);unsafe = (Unsafe) field.get(null);objectRefSize = unsafe.arrayIndexScale(Object[].class);} catch (Exception e) {throw new RuntimeException(e);}}public int getObjectRefSize() {return objectRefSize;}/** Sizes of all primitive values */private static final Map<Class, Integer> primitiveSizes;static {primitiveSizes = new HashMap<Class, Integer>(10);primitiveSizes.put(byte.class, 1);primitiveSizes.put(char.class, 2);primitiveSizes.put(int.class, 4);primitiveSizes.put(long.class, 8);primitiveSizes.put(float.class, 4);primitiveSizes.put(double.class, 8);primitiveSizes.put(boolean.class, 1);}/*** Get object information for any Java object. Do not pass primitives to* this method because they will boxed and the information you will get will* be related to a boxed version of your value.* * @param obj*            Object to introspect* @return Object info* @throws IllegalAccessException*/public ObjectInfo introspect(final Object obj)throws IllegalAccessException {try {return introspect(obj, null);} finally { // clean visited cache before returning in order to make// this object reusablem_visited.clear();}}// we need to keep track of already visited objects in order to support// cycles in the object graphsprivate IdentityHashMap<Object, Boolean> m_visited = new IdentityHashMap<Object, Boolean>(100);private ObjectInfo introspect(final Object obj, final Field fld)throws IllegalAccessException {// use Field type only if the field contains null. In this case we will// at least know what's expected to be// stored in this field. Otherwise, if a field has interface type, we// won't see what's really stored in it.// Besides, we should be careful about primitives, because they are// passed as boxed values in this method// (first arg is object) - for them we should still rely on the field// type.boolean isPrimitive = fld != null && fld.getType().isPrimitive();boolean isRecursive = false; // will be set to true if we have already// seen this objectif (!isPrimitive) {if (m_visited.containsKey(obj))isRecursive = true;m_visited.put(obj, true);}final Class type = (fld == null || (obj != null && !isPrimitive)) ? obj.getClass() : fld.getType();int arraySize = 0;int baseOffset = 0;int indexScale = 0;if (type.isArray() && obj != null) {baseOffset = unsafe.arrayBaseOffset(type);indexScale = unsafe.arrayIndexScale(type);arraySize = baseOffset + indexScale * Array.getLength(obj);}final ObjectInfo root;if (fld == null) {root = new ObjectInfo("", type.getCanonicalName(), getContents(obj,type), 0, getShallowSize(type), arraySize, baseOffset,indexScale);} else {final int offset = (int) unsafe.objectFieldOffset(fld);root = new ObjectInfo(fld.getName(), type.getCanonicalName(),getContents(obj, type), offset, getShallowSize(type),arraySize, baseOffset, indexScale);}if (!isRecursive && obj != null) {if (isObjectArray(type)) {// introspect object arraysfinal Object[] ar = (Object[]) obj;for (final Object item : ar)if (item != null)root.addChild(introspect(item, null));} else {for (final Field field : getAllFields(type)) {if ((field.getModifiers() & Modifier.STATIC) != 0) {continue;}field.setAccessible(true);root.addChild(introspect(field.get(obj), field));}}}root.sort(); // sort by offsetreturn root;}// get all fields for this class, including all superclasses fieldsprivate static List<Field> getAllFields(final Class type) {if (type.isPrimitive())return Collections.emptyList();Class cur = type;final List<Field> res = new ArrayList<Field>(10);while (true) {Collections.addAll(res, cur.getDeclaredFields());if (cur == Object.class)break;cur = cur.getSuperclass();}return res;}// check if it is an array of objects. I suspect there must be a more// API-friendly way to make this check.private static boolean isObjectArray(final Class type) {if (!type.isArray())return false;if (type == byte[].class || type == boolean[].class|| type == char[].class || type == short[].class|| type == int[].class || type == long[].class|| type == float[].class || type == double[].class)return false;return true;}// advanced toString logicprivate static String getContents(final Object val, final Class type) {if (val == null)return "null";if (type.isArray()) {if (type == byte[].class)return Arrays.toString((byte[]) val);else if (type == boolean[].class)return Arrays.toString((boolean[]) val);else if (type == char[].class)return Arrays.toString((char[]) val);else if (type == short[].class)return Arrays.toString((short[]) val);else if (type == int[].class)return Arrays.toString((int[]) val);else if (type == long[].class)return Arrays.toString((long[]) val);else if (type == float[].class)return Arrays.toString((float[]) val);else if (type == double[].class)return Arrays.toString((double[]) val);elsereturn Arrays.toString((Object[]) val);}return val.toString();}// obtain a shallow size of a field of given class (primitive or object// reference size)private static int getShallowSize(final Class type) {if (type.isPrimitive()) {final Integer res = primitiveSizes.get(type);return res != null ? res : 0;} elsereturn objectRefSize;}static class ObjectInfo {/** Field name */public final String name;/** Field type name */public final String type;/** Field data formatted as string */public final String contents;/** Field offset from the start of parent object */public final int offset;/** Memory occupied by this field */public final int length;/** Offset of the first cell in the array */public final int arrayBase;/** Size of a cell in the array */public final int arrayElementSize;/** Memory occupied by underlying array (shallow), if this is array type */public final int arraySize;/** This object fields */public final List<ObjectInfo> children;public ObjectInfo(String name, String type, String contents,int offset, int length, int arraySize, int arrayBase,int arrayElementSize) {this.name = name;this.type = type;this.contents = contents;this.offset = offset;this.length = length;this.arraySize = arraySize;this.arrayBase = arrayBase;this.arrayElementSize = arrayElementSize;children = new ArrayList<ObjectInfo>(1);}public void addChild(final ObjectInfo info) {if (info != null)children.add(info);}/*** Get the full amount of memory occupied by a given object. This value* may be slightly less than an actual value because we don't worry* about memory alignment - possible padding after the last object* field.* * The result is equal to the last field offset + last field length +* all array sizes + all child objects deep sizes* * @return Deep object size*/public long getDeepSize() {// return length + arraySize + getUnderlyingSize( arraySize != 0 );return addPaddingSize(arraySize + getUnderlyingSize(arraySize != 0));}long size = 0;private long getUnderlyingSize(final boolean isArray) {// long size = 0;for (final ObjectInfo child : children)size += child.arraySize+ child.getUnderlyingSize(child.arraySize != 0);if (!isArray && !children.isEmpty()) {int tempSize = children.get(children.size() - 1).offset+ children.get(children.size() - 1).length;size += addPaddingSize(tempSize);}return size;}private static final class OffsetComparator implementsComparator<ObjectInfo> {@Overridepublic int compare(final ObjectInfo o1, final ObjectInfo o2) {return o1.offset - o2.offset; // safe because offsets are small// non-negative numbers}}// sort all children by their offsetpublic void sort() {Collections.sort(children, new OffsetComparator());}@Overridepublic String toString() {final StringBuilder sb = new StringBuilder();toStringHelper(sb, 0);return sb.toString();}private void toStringHelper(final StringBuilder sb, final int depth) {depth(sb, depth).append("name=").append(name).append(", type=").append(type).append(", contents=").append(contents).append(", offset=").append(offset).append(", length=").append(length);if (arraySize > 0) {sb.append(", arrayBase=").append(arrayBase);sb.append(", arrayElemSize=").append(arrayElementSize);sb.append(", arraySize=").append(arraySize);}for (final ObjectInfo child : children) {sb.append('\n');child.toStringHelper(sb, depth + 1);}}private StringBuilder depth(final StringBuilder sb, final int depth) {for (int i = 0; i < depth; ++i)sb.append("\t");return sb;}private long addPaddingSize(long size) {if (size % 8 != 0) {return (size / 8 + 1) * 8;}return size;}}

当我们需要计算一个对象大小时,我们只需要获取ClassIntrospector实例,并调用其introspect方法,参数为需要计算大小的对象,就可以获取到ObjectInfo对象,这个对象中就包含了要计算的对象的各项信息(名字,类型,属性的偏移量等),想要获取对象的大小,我们只需要调用OjbectInfo的getDeepSiz即可。
ClassIntrospector中还定义了一个方法getObjectRefSize,这个方法的作用是获取当前虚拟机对象引用指针所占的空间,如果机器的内存在32G以下,则会默认开启指针压缩,占4个字节,否则占8个,可以使用参数-XX:-UseCompressedOops进行指针压缩

下面我们进行一个简单的验证:

首先我们先定义一个对象:

public class Person {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}

测试代码:

public static void main(String[] args) throws Exception {Person person = new Person();System.out.println(SizeOfAgent.fullSizeOf(person));ClassIntrospector cIntrospector = new ClassIntrospector();ObjectInfo oInfo = cIntrospector.introspect(person);System.out.println(oInfo.getDeepSize());
}

运行结果:

24
24

两种方法的运行结果一致,我们进行对象的手动计算,计算公式:
mark头(8字节)+oop指针(4字节)+对象属性
以person对象为例:
mark头(8字节)+oop指针(4字节)+name(String类型引用4字节)+age(int类型引用4字节)=20
jvm会对一个对象进行内存对齐,是的对象的大小为8的倍数,所以最终结果为24。

当然这两种计算方式都是对对象的一个大概计算,当一个对象引用String类型时,其实是有常量池的存在的,所以计算出来的只我们只能做个参考即可。

jvm两种方式获取对象所占用的内存相关推荐

  1. android手机两种方式获取IP地址

    http://www.cnblogs.com/android100/p/Android-get-ip.html 1.使用WIFI 首先设置用户权限 Xml代码   <uses-permissio ...

  2. java 实现BufferedImage和ImageReader两种方式获取图片宽高、判断图片类型、获取图片大小工具类代码以及测试响应结果

    源码: import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO; import java ...

  3. Java面试题:synchronized和对象的访问定位的两种方式

    说一说自己对于 synchronized 关键字的理解 ? synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者 代码块在任意时 ...

  4. Java常见面试题:对象的访问定位的两种方式

    对象的访问定位的两种方式 java对象在访问的时候,我们需要通过java虚拟机栈的reference类型的数据去操作具体的对象. 由于reference类型在java虚拟机规范中只规定了一个对象的引用 ...

  5. 获取checkbox选中状态的两种方式_张童瑶的博客

    获取checkbox选中状态的两种方式 获取checkbox选中状态的两种方式 第一种方式 第二种方式 我的其他文章 我的其他网站 获取checkbox选中状态的两种方式 我在开发项目的时候遇到这个问 ...

  6. 七牛云图床开启HTTPS域名的两种方式

    我的原文:https://hijerry.cn/p/64756.html https下的http图片 因为自己一直用的是七牛云来管理博客图片,发现在chrome下用https访问博客时看不到博客里的图 ...

  7. 【Android】Android 彩信发送的两种方式+源代码

    Android  彩信发送的两种方式 第一种:直接调用彩信发送接口 实现代码如下, Intent intent = new Intent(Intent.ACTION_SEND); intent.add ...

  8. java实例化字符串两种方式区别

    一:实例化字符串对象的两种方式的区别 这个知识点是面试中的一个经久不衰的问题,.也是一个比较麻烦的问题,对于许多同学来说也是难点,本次课我们会详细的分析.上次课说了创建字符串对象的两种方式:直接赋值( ...

  9. 聊聊JVM(三)两种计算Java对象大小的方法

    普通对象的结构如下,按64位机器的长度计算 1. 对象头(_mark), 8个字节 2. Oop指针,如果是32G内存以下的,默认开启对象指针压缩,4个字节 3. 数据区 4.Padding(内存对齐 ...

最新文章

  1. jquery计算表格列,求和
  2. Spring mvc-kaptcha 验证码
  3. TortoiseGit入门(图文教程) Git,Github,puttygen,SSH
  4. Google Map API V3调用arcgis发布的瓦片地图服务
  5. 【转】秒杀系统架构分析与实战
  6. vant 下拉框样式_使用 Vue 的 Vant.js List 列表组件实现无限下拉
  7. 用Unity3D实现智能巡逻兵游戏
  8. 使您的Java 8方法引用生效
  9. C# 打开文件/跳转链接
  10. 腾讯联手国家信息中心启动共筑疫情“数据长城”计划
  11. Matlab之双坐标轴绘制plotyy
  12. linux mantis安装 yum,CentOS 安装和配置 Mantis
  13. vs2017 项目开发 解决方案下的多个项目
  14. 常见的蔬菜(vegetables)英语单词:
  15. 员工缺乏责任心的四大原因
  16. IO流-常用的IO流总结
  17. 详解 atoi 函数并模拟实现
  18. Mac上安装mysql及密码重置
  19. 机器学习三大基本任务_Task01
  20. vue + echars地图 省市区 + 添加点标记

热门文章

  1. 为什么不应该使用“volatile”类型
  2. Linux中逻辑卷相关知识简介
  3. WCF .net Tcp 错误异常
  4. utf8 连接 mysql_mysql配置为支持utf8的连接方式只要改client就可以了吗
  5. Python爬虫之破解百度翻译--requests案例详解(一)
  6. php 多条查询结果插入新表,Mysql应用MySQL查询结果复制到新表的方法(更新、插入)...
  7. java获取焦点的组件_JAVA组件焦点的特性:获取组件时其顶层组件必须为可见的...
  8. _linux运维正确安装oracle流程
  9. python argv 详解_对python中的argv和argc使用详解
  10. vscode 无法跳转到函数定义_玩转VS Code