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/
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
打印出某个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
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。