前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java计算一个对象占用内存的大小

Java计算一个对象占用内存的大小

作者头像
用户7886150
修改2020-12-04 17:16:26
2K0
修改2020-12-04 17:16:26
举报
文章被收录于专栏:bit哲学院

参考链接: Java对象如何存储在内存中

在C/C++中计算某一个基本类型或者对象占用内存大小的方法很简单,只要调用库里面的sizeof()操作符即可,但是在Java的API里面并没有给我们提供类似的方法。那么我们可不可以自己实现一个Java中的sizeof()方法呢?答案是肯定的。为了计算一个Java对象占用内存的大小,首先你得对Java对象的内存结构有所了解。如果你还不了解,请先阅读Java内存结构。 

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

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

</pre><pre name="code" class="java">package size;

import java.lang.reflect.Array;

import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

import sun.misc.Unsafe;

public class UnsafeTest {

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

    private static final int OBJECT_HEADER_SIZE = 8;

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

    private static final int MINIMUM_OBJECT_SIZE = 8;

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

    private static final int OBJECT_ALIGNMENT = 8;

    public static long sizeOf(Object obj) {

        // 获得Unsafe实例

        Unsafe unsafe;

        try {

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

            unsafeField.setAccessible(true);

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

        } catch (Throwable t) {

            unsafe = null;

        }

        // 判断对象是否为数组

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

            Class<?> klazz = obj.getClass();

            int base = unsafe.arrayBaseOffset(klazz);

            int scale = unsafe.arrayIndexScale(klazz);

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

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

                size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);

            }

            return Math.max(MINIMUM_OBJECT_SIZE, size);

        } else {

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

            for (Class<?> klazz = obj.getClass(); klazz != null; klazz = klazz

                    .getSuperclass()) {

                long lastFieldOffset = -1;

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

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

                        lastFieldOffset = Math.max(lastFieldOffset,

                                unsafe.objectFieldOffset(f));

                    }

                }

                if (lastFieldOffset > 0) {

                    lastFieldOffset += 1;

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

                        lastFieldOffset += OBJECT_ALIGNMENT

                                - (lastFieldOffset % OBJECT_ALIGNMENT);

                    }

                    return Math.max(MINIMUM_OBJECT_SIZE, lastFieldOffset);

                }

            }

            // 该对象没有任何属性

            long size = OBJECT_HEADER_SIZE;

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

                size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);

            }

            return Math.max(MINIMUM_OBJECT_SIZE, size);

        }

    }

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

        OpusVoiceInfo voice = new OpusVoiceInfo();

        OpusVoiceInfo voicex = new OpusVoiceInfo();

        voicex.setOpusId(8888888888888888888L);

        voicex.setOpusId2(8888888888888888888L);

        voicex.setOpusId3(8888888888888888888L);

        voicex.setOpusId4(8888888888888888888L);

        OpusVoiceInfo2 voice2 = new OpusVoiceInfo2();

        System.out.println(UnsafeTest.sizeOf(voice));// 输出

        System.out.println(UnsafeTest.sizeOf(voicex));

        System.out.println("voice2:" + UnsafeTest.sizeOf(voice2));// 输出

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

    }

}

/**

output:

72

72

voice2:56

*/

jmap 查看 一致的:OK  

jmap -histo PID | findstr ObjName 

 打印出某个java进程(使用pid)内存内的,所有‘对象’的情况(如:产生那些对象,及其数量)。 

 可以输出所有内存中对象的工具,甚至可以将VM 中的heap,以二进制输出成文本。使用方法 jmap -histo pid。如果连用SHELL jmap -histo pid>a.log可以将其保存到文本中去,在一段时间后,使用文本对比工具,可以对比出GC回收了哪些对象。jmap -dump:format=b,file=outfile 3024可以将3024进程的内存heap输出出来到outfile文件里,再配合MAT(内存分析工具(Memory Analysis Tool),使用参见:http://blog.csdn.net/fenglibing/archive/2011/04/02/6298326.aspx)或与jhat (Java Heap Analysis Tool)一起使用,能够以图像的形式直观的展示当前内存是否有问题。 

 64位机上使用需要使用如下方式: 

jmap -J-d64 -heap pid 

 2、命令格式 

 SYNOPSIS 

 jmap [ option ] pid 

 jmap [ option ] executable core 

 jmap [ option ] [server-id@]remote-hostname-or-IP 

 3、参数说明 

 1)options:  

 executable Java executable from which the core dump was produced. 

 (可能是产生core dump的java可执行程序) 

 core 将被打印信息的core dump文件 

 remote-hostname-or-IP 远程debug服务的主机名或ip 

 server-id 唯一id,假如一台主机上多个远程debug服务  

 2)基本参数: 

 -dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件.  

 -finalizerinfo 打印正等候回收的对象的信息. 

 -heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况. 

 -histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量.  

 -permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来.  

 -F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效.  

 -h | -help 打印辅助信息  

 -J 传递参数给jmap启动的jvm.  

 pid 需要被打印配相信息的java进程id,创业与打工的区别 - 博文预览,可以用jps查问. 

 4、使用示例 

 1)[fenglb@ccbu-156-5 ~]$ jmap -histo 4939 

 [输出较多这里不贴了] 

 2)[fenglb@ccbu-156-5 ~]$ jmap -dump:format=b,file=test.bin 4939 

 Dumping heap to /home/fenglb/test.bin ... 

 Heap dump file created

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档