UDN-企业互联网技术人气社区

板块导航

浏览  : 1409
回复  : 0

[讨论交流] Java计算一个对象占用内存的大小

[复制链接]
呵呵燕的头像 楼主
发表于 2016-11-11 22:11:11 | 显示全部楼层 |阅读模式
  在C/C++中计算某一个基本类型或者对象占用内存大小的方法很简单,只要调用库里面的sizeof()操作符即可,但是在Java的API里面并没有给我们提供类似的方法。那么我们可不可以自己实现一个Java中的sizeof()方法呢?答案是肯定的。为了计算一个Java对象占用内存的大小,首先你得对Java对象的内存结构有所了解。如果你还不了解,请先阅读Java内存结构。

  首先介绍一下sun.misc.Unsafe类,该类是Java中很神奇的一个类,这个类是用于执行低级别、不安全操作的方法集合。尽管这个类和所有的方法都是公开的(public),但是这个类的使用仍然受限,你无法在自己Java程序中直接使用该类,因为它的构造函数是私有的(private)。但是我们仍然能获得它的实例,方法如下:

  1. Unsafe unsafe;

  2.   try {

  3.   Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");

  4.   unsafeField.setAccessible(true);

  5.   unsafe = (Unsafe)unsafeField.get(null);

  6.   } catch (Throwable t) {

  7.   unsafe = null;

  8.   }
复制代码


  Unsafe类的更多介绍和用法可以参照http://mishadoff.github.io/blog/ ... ot-misc-dot-unsafe/

  我们将使用Unsafe类的如下方法来实现Java中的sizeof()

  1. //获得对象中一个非静态字段的偏移量

  2.   public native long objectFieldOffset(java.lang.reflect.Field field);

  3.   //获得数组对象第一个元素的偏移量

  4.   public native int arrayBaseOffset(java.lang.Class aClass);

  5.   //获得数组每一个元素所占内存大小

  6.   public native int arrayIndexScale(java.lang.Class aClass);
复制代码


  sizeof()方法代码如下:

  注意:不同的Java虚拟机,参数会各不一样,以下代码只适用于32位的HotSpot虚拟机。

  1. package test;

  2.   import java.lang.reflect.Array;

  3.   import java.lang.reflect.Field;

  4.   import java.lang.reflect.Modifier;

  5.   import sun.misc.Unsafe;

  6.   public class JUtil {

  7.   /**对象头部的大小 */

  8.   private static final int OBJECT_HEADER_SIZE = 8;

  9.   /**对象占用内存的最小值*/

  10.   private static final int MINIMUM_OBJECT_SIZE = 8;

  11.   /**对象按多少字节的粒度进行对齐*/

  12.   private static final int OBJECT_ALIGNMENT = 8;

  13.   public static long sizeOf(Object obj) {

  14.   //获得Unsafe实例

  15.   Unsafe unsafe;

  16.   try {

  17.   Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");

  18.   unsafeField.setAccessible(true);

  19.   unsafe = (Unsafe)unsafeField.get(null);

  20.   } catch (Throwable t) {

  21.   unsafe = null;

  22.   }

  23.   //判断对象是否为数组

  24.   if (obj.getClass().isArray()) {

  25.   Class klazz = obj.getClass();

  26.   int base = unsafe.arrayBaseOffset(klazz);

  27.   int scale = unsafe.arrayIndexScale(klazz);

  28.   long size = base + (scale * Array.getLength(obj));

  29.   if ((size % OBJECT_ALIGNMENT) != 0) {

  30.   size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);

  31.   }

  32.   return Math.max(MINIMUM_OBJECT_SIZE, size);

  33.   } else {

  34.   //如果数组对象则迭代遍历该对象的父类,找到最后一个非静态字段的偏移量

  35.   for (Class klazz = obj.getClass(); klazz != null; klazz = klazz.getSuperclass()) {

  36.   long lastFieldOffset = -1;

  37.   for (Field f : klazz.getDeclaredFields()) {

  38.   if (!Modifier.isStatic(f.getModifiers())) {

  39.   lastFieldOffset = Math.max(lastFieldOffset, unsafe.objectFieldOffset(f));

  40.   }

  41.   }

  42.   if (lastFieldOffset > 0) {

  43.   lastFieldOffset += 1;

  44.   if ((lastFieldOffset % OBJECT_ALIGNMENT) != 0) {

  45.   lastFieldOffset += OBJECT_ALIGNMENT - (lastFieldOffset % OBJECT_ALIGNMENT);

  46.   }

  47.   return Math.max(MINIMUM_OBJECT_SIZE, lastFieldOffset);

  48.   }

  49.   }

  50.   //该对象没有任何属性

  51.   long size = OBJECT_HEADER_SIZE;

  52.   if ((size % OBJECT_ALIGNMENT) != 0) {

  53.   size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);

  54.   }

  55.   return Math.max(MINIMUM_OBJECT_SIZE, size);

  56.   }

  57.   }

  58.   public static void main(String[] args) throws InterruptedException {

  59.   System.out.println(JUtil.sizeOf(new MyClass()));//输出32

  60.   Thread.sleep(100000);//阻塞线程,为了使用jmap工具

  61.   }

  62.   }
复制代码


  下面测试所用的类:

  1. package test;  
  2.   
  3. public class MyClass {  
  4.     private byte a;  
  5.     private int c;  
  6.     private boolean d;  
  7.     private long e;  
  8.     private Object f;   
  9. }  
复制代码


  最后我们使用jmap工具来检查计算的结果是否正确

k.jpg


  上图中的各项含义分别是:类的编号,实例的个数,占用内存的大小,类的名字。

  可以看出sizeof()方法计算出来的结果是完全正确的。

原文作者:佚名  来源:开发者头条

相关帖子

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部