在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 删除。