`

JVM对象占用内存计算工具--SizeOf源码分析

 
阅读更多

SizeOf是一个用于计算JVM堆中对象所占用内存的小工具,使用起来很方便。今天没事把源码down下来看了看,同时写了个笔记贴上来,希望对看到的人有所帮助。大笑

-----------------------------------

SizeOf主要思路是通过jdk1.5提供的新特性Instrumentation计算对象占用内存大小。

jdk文档中java.lang.instrument.Instrumentation介绍:

此类提供检测 Java 编程语言代码所需的服务。检测是向方法中添加字节码,以搜集各种工具所使用的数据。由于更改完全是进行添加,所以这些工具不修改应用程序的状态或行为。这种无害工具的例子包括镜像代理、分析器、覆盖分析器和事件记录器。

源码分析

项目主页:http://sourceforge.net/projects/sizeof/ 

整个源码只有一个类SizeOf.java,重点是sizeOf和deepSizeOf两个方法。

1、premain

通过premain注入VM中Instrumentation实例。

private static Instrumentation inst;
public static void premain(String options, Instrumentation inst)
{
	SizeOf.inst = inst;
	System.out.println("JAVAGENT: call premain instrumentation for class SizeOf");
}
JVM以代理类方式启动时,回调premain方法,并注入Instrumentation实例。

2、sizeOf:

计算一个对象的大小,但该对象内引用的其它对象的大小不计算在内。

首先,SizeOf类内有3个与计算对象大小相关的开关:

private static boolean SKIP_STATIC_FIELD = false;//是否计算static属性
private static boolean SKIP_FINAL_FIELD = false;//是否计算final属性
private static boolean SKIP_FLYWEIGHT_FIELD = false;//是否计算共享(享元)对象

sizeOf方法:

public static long sizeOf(Object object)
{
	if (inst == null)
		throw new IllegalStateException("Instrumentation is null");
	//是否计算共享对象
	if (SKIP_FLYWEIGHT_FIELD && isSharedFlyweight(object))
		return 0;
	return inst.getObjectSize(object);
}

该方法内部直接通过Instrumentation的getObjectSize方法计算当前传入对象占用内存的大小,需要注意的是,这里有一个isSharedFlyweight方法:

private static boolean isSharedFlyweight(Object obj)
{
		// optimization - all of our flyweights are Comparable
		if (obj instanceof Comparable)
		{
			if (obj instanceof Enum)
			{
				return true;
			} else if (obj instanceof String)
			{
				return (obj == ((String) obj).intern()); 
			} else if (obj instanceof Boolean)
			{
				return (obj == Boolean.TRUE || obj == Boolean.FALSE);
			} else if (obj instanceof Integer)
			{
				return (obj == Integer.valueOf((Integer) obj)); 
			} else if (obj instanceof Short)
			{
				return (obj == Short.valueOf((Short) obj));
			} else if (obj instanceof Byte)
			{
				return (obj == Byte.valueOf((Byte) obj));
			} else if (obj instanceof Long)
			{
				return (obj == Long.valueOf((Long) obj));
			} else if (obj instanceof Character)
			{
				return (obj == Character.valueOf((Character) obj));
			}
		}
		return false;
}

该方法的作用是判断传入对象是否为享元对象,可以理解为共享一块内存区域的对象,这部分内存可以不进行计算统计(因为每个对象都共同使用一块内存)。所有享元对象都实现了Comparable接口。

(1)关于String.intern():String类内部维护着一个字符串池,如果池中包含一个等于此字符串的对象,则返回池中对象,否则将此String对象添加到池中,并返回此String对象的引用。intern()方法会从字符串池中取出与这个字符串内容相同的字符串对象,也就是说String.intern()返回的是空享内存空间的对象。

(2)关于基本数据类型

至于Boolean、Integer、Short、Byte...等基本数据类型包装的对象怎么也会被算作共享内存区域呢?查了一些资料,原因是这样的:就拿Integer类来说,它会缓存了-128到127这256个状态的Integer值,如果使用Integer.valueOf(int i),传入的int范围正好在此内,就返回静态实例。

也就是说,如果你使用了Integer.valueOf()时,有可能直接返回静态实例。

可见在日常编程中,能用Boolean.TRUE或FALSE就不要用new Boolean(),能用Integer.valueOf的时候就不要用new Integer(),其它各数据类型也都如此。

如果传入对象内部属性有引用其它对象时,sizeOf方法不计算引用对象的大小(详见deepSizeOf)。

3、deepSizeOf方法:

public static long deepSizeOf(Object objectToSize)
{
	//Set<Integer> doneObj = new HashSet<Integer>();
	Map<Object,Object> doneObj = new IdentityHashMap<Object,Object>();
	return deepSizeOf(objectToSize, doneObj, 0);
}

首先声明了一个IdentityHashMap,jdk文档中关于IdentityHashMap的解释:

此类利用哈希表实现Map接口,比较键(和值)时使用引用相等性代替对象相等性。换句话说,在 IdentityHashMap中,当且仅当(k1==k2)时,才认为两个键k1和k2相等。

然后是deepSizeOf的重载方法,这个方法有点长,分段来解析。

private static long deepSizeOf(Object o, Map<Object,Object> doneObj, int depth)
{
		if (o == null)
		{
			if (debug)
				print("null\n");
			return 0;
		}
		long size = 0;
		if (doneObj.containsKey(o))
		{
			if (debug)
				print("\n%s{ yet computed }\n", indent(depth));
			return 0;
		}
		if (debug)
			print("\n%s{ %s\n", indent(depth), o.getClass().getName());
		doneObj.put(o,null);//从引用map中获取该属性
		size = sizeOf(o);//计算obj的大小
                //...
如果传入对象的引用在IdentityHashMap中已经包含,说明已经计算过该对象,则返回0,否则直接将该引用放入IdentityHashMap之后直接调用sizeOf方法计算对象大小。
if (o instanceof Object[])//对象为数组
{
	int i = 0;
	for (Object obj : (Object[]) o)
	{
		if (debug)
		print("%s [%d] = ", indent(depth), i++);
		//取数组中的每个元素,递归调用
		size += deepSizeOf(obj, doneObj, depth + 1);
	}
} else
 如果对象为数组,则取数组的每个元素递归调用deepSizeOf方法,计算每个对象大小。
else //该对象不为数组
{
	//获取每个属性
	Field[] fields = o.getClass().getDeclaredFields();
	//遍历每个属性
	for (Field field : fields)
	{
		//值为true则指示反射的对象在使用时应该取消Java语言访问检查
		field.setAccessible(true);
		Object obj;
		try
		{
			obj = field.get(o);//取出该属性在对象上的值
		} catch (IllegalArgumentException e)
		{
			throw new RuntimeException(e);
		} catch (IllegalAccessException e)
		{
			throw new RuntimeException(e);
		}
		//判断该属性是否可计算
		if (isComputable(field))
		{
			if (debug)
				print("%s %s = ", indent(depth), field.getName());
			size += deepSizeOf(obj, doneObj, depth + 1);//递归调用计算各属性对象大小
		} else
		{
			if (debug)
				print("%s %s = %s\n", indent(depth), field.getName(), obj.toString());
		}
	}
}
如果传入对象不为数组,则遍历每个属性,取出每个属性在对象上的值,然后判断该属性是否可被计算,如果可以,则递归调用deepSizeOf方法,累加size大小。

这里有一个方法isComputable如下:

private static boolean isComputable(Field field)
{
		int modificatori = field.getModifiers();
		if (isAPrimitiveType(field.getType()))
			return false;
		else if (SKIP_STATIC_FIELD && Modifier.isStatic(modificatori))
			return false;
		else if (SKIP_FINAL_FIELD && Modifier.isFinal(modificatori))
			return false;
		else
			return true;
}

对3种类型的属性进行判断:原始类型的包装类型、static类型和final类型。其中后两种可有开关控制,第一种由于在sizeOf方法中的getObjectSize计算过了,此处不需要重复计算。

至此,deepSizeOf部分的代码也分析完了。

结束语:

今天对SizeOf的源码分析就先告一段落,希望对看到的人有所帮助。

关于开源工具SizeOf,这里还有我的另一篇Blog,欢迎多提些建议。

使用SizeOf测定JVM中对象占用内存: http://shensy.iteye.com/blog/1765760

 

分享到:
评论

相关推荐

    测定JVM中对象占用内存—SizeOf

    原项目下载地址:http://sourceforge.net/projects/sizeof/ ...1、将SizeOf.jar放到Eclipse工程路径下,添加到classpath中; 2、运行前添加VM参数:-javaagent:lib/SizeOf.jar 运行即可(将jar放在lib路径下)。

    jvm内存分析-jdk17-memoryAnalyer

    在"jvm内存分析-jdk17-memoryAnalyzer"这个主题中,我们将深入探讨JVM内存结构,特别是针对Java 17版本的内存配置和分析工具Memory Analyzer (MAT)。 JVM内存主要分为以下几个区域: 1. **堆内存**:这是Java程序...

    mat(mac)---jvm内存分析工具

    MAT,全称Memory Analyzer Tool,是IBM开发的一款强大的Java虚拟机(JVM)内存分析工具,尤其适用于Mac OS X平台。这款工具可以帮助开发者诊断和解决Java应用中的内存泄漏问题,提高应用性能。MAT通过深入分析堆转储...

    JVM调优总结 Xms -Xmx -Xmn -Xss

    - **作用**:限制JVM能够使用的最大内存空间,防止程序占用过多资源导致系统不稳定或崩溃。 - **示例**:`java -Xmx3550m ...` #### 3. -Xmn(Young Generation Size) - **定义**:设置年轻代(Young Generation...

    JVM调优总结 -Xms -Xmx -Xmn -Xss

    -Xmx 设置 JVM 的最大可用内存,-Xms 设置 JVM 的初始内存大小。二者的值可以相同,以避免每次垃圾回收完成后 JVM 重新分配内存。 -Xmn 设置年轻代的大小,整个 JVM 内存大小=年轻代大小 + 年老代大小 + 持久代...

    JVM-内存管理 2012-12.pdf

    内存分析工具如jvisualvm、jmap、jhat和MAT(Memory Analyzer Tool)等,为Java开发者提供了分析JVM内存使用状况的能力。这些工具可以用来监控堆内存的使用情况,识别内存泄漏,分析内存使用趋势,帮助开发者进行...

    (二)MATJVM 内存分析工具.MAT JVM 内存分析工具.MAT JVM 内存分析工具.

    MAT JVM内存分析工具可以帮助开发者深入理解Java虚拟机(JVM)的内存管理机制,通过分析堆内存快照来识别内存消耗异常的情况。 首先,MAT提供了丰富的视图来帮助用户查看内存状态,例如“概述”视图可以快速了解堆...

    Java虚拟机-jvm故障诊断与性能优化-源码

    - **内存分析**:检查是否存在内存泄漏,以及对象生命周期是否正常。 - **锁竞争**:分析线程竞争状态,优化同步机制。 7. **JVM内存溢出** - **堆溢出**:过多的对象无法分配到内存中,可通过调整堆大小或优化...

    java性能调优-jvm创建对象和-performance-createobj-allocationmemory.zip

    在Java性能调优中,JVM(Java Virtual Machine)创建对象和内存分配是至关重要的环节。这直接影响到程序的运行速度、内存占用以及整体系统的稳定性。本篇文章将深入探讨Java中对象创建的过程、内存分配机制,以及...

    计算对象占用内存空间ObjectSize-master.zip

    6. **内存分析工具**:除了手动计算外,开发者还可以使用各种内存分析工具,如JVisualVM、MAT(Memory Analyzer Tool)等,它们能更直观地显示对象及其关联的内存占用情况。 7. **内存优化**:通过分析对象大小,...

    java jvm 参数 -Xms -Xmx -Xmn -Xss -

    同时,监控系统的CPU使用率、内存占用和GC日志也是关键,以便找出可能导致性能瓶颈或内存泄漏的线索。 在进行JVM调优时,我们还可以考虑其他参数,比如设置不同GC算法(如Parallel GC、G1 GC或ZGC),以及调整并行...

    JVM内存管理-GC模型-编写GC友好的代码.pdf

    - **避免大对象**: 大对象会占用较多的内存空间,导致垃圾回收时间变长。 - **显式地释放不再使用的资源**: 例如关闭文件流或数据库连接等,以减少垃圾回收的压力。 - **使用对象池**: 对于频繁创建和销毁的对象,...

    JVM内存模型-内存结构-内存屏障

    主要为大家讲解JVM内存模型|内存结构|内存屏障,他们的概念,有什么关联以及各种的功能

    ibm-java-堆内存分析工具-heapanalyzer

    IBM Java堆内存分析工具——HeapAnalyzer,是一款专为IBM J9 VM设计的强大内存分析工具,它可以帮助开发者深入理解Java应用程序的内存使用情况,检测并解决内存泄漏问题,从而提升应用性能。本文将详细介绍Heap...

    JVM的调优总结----------

    - **清除阶段**:释放未被标记的对象占用的空间。 - 缺点是会产生碎片化,并且暂停时间较长。 2. **复制(Copying)**: - 将内存分成两块相等的部分,每次只使用其中一块,垃圾回收时将存活的对象复制到另一块...

    JVM内存参数详解以及配置调优

    内存管理是 JVM 中最重要的组件之一,它负责为对象分配内存和释放内存。垃圾回收(Garbage Collector,GC)是内存管理的主要组件,它负责回收堆中不再使用的对象,以释放内存。垃圾回收有两种方式:Minor 收集和 ...

    jvm内存分析工具mat

    MAT,全称Memory Analyzer Tool,是IBM开发的一款强大的Java虚拟机(JVM)内存分析工具。它主要用于诊断Java应用程序的内存泄漏问题,帮助开发者理解内存消耗情况,优化内存配置,从而提升应用性能。MAT以其易用性和...

    jvm内存分析工具mat安装包

    MAT,全称Memory Analyzer Tool,是IBM开发的一款强大的JVM内存分析工具,尤其适用于诊断Java应用程序的内存泄漏问题。在Java开发过程中,内存溢出(Out Of Memory)问题常常会导致程序异常终止,而MAT就是解决这类...

    idea插件JVM内存工具JProfiler11

    IntelliJ IDEA(简称Idea)作为广受欢迎的Java集成开发环境,提供了一系列强大的工具来帮助开发者进行性能调优,其中就包括JProfiler11这款强大的JVM内存分析工具。本文将详细介绍如何使用Idea中的JProfiler11插件,...

Global site tag (gtag.js) - Google Analytics