b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

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

电脑杂谈  发布时间:2020-12-21 07:04:59  来源:网络整理

SizeOf是一个小型工具,用于计算JVM堆中的对象所占用的内存,使用起来非常方便。今天,我可以轻松地看一下源代码,同时我写了便笺并发布了它,希望对看到它的人有所帮助。

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

SizeOf的主要思想是通过jdk1.5提供的新功能Instrumentation计算对象的内存大小。

jdk文档中的java.lang.instrument.Instrumentation简介:

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

源代码分析:

项目主页:

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

1、premain:

通过premain将Instrumentation实例注入VM。

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个与计算对象大小有关的开关:

java分析内存占用的工具_java分析内存占用的工具

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;
}

此方法的功能是判断传入的对象是否是一个举重对象,可以将其理解为共享存储区的对象。可以计算该部分内存而无需计算(因为每个对象都使用一个公共内存)。所有flyweight对象都实现Comparable接口。

([1)About String.intern():String类在内部维护一个字符串池。如果该池包含与此字符串相等的对象,则返回该池中的对象,否则将String对象添加到pool,并返回对此String对象的引用intern()方法将从字符串池中取出与该字符串内容相同的字符串对象,这意味着String.intern()返回具有可用内存空间的对象。

([2)关于基本数据类型:

对于由基本数据类型(例如Boolean,Integer,Short,Byte等)包装的对象,如何将它们计为共享内存区域?检查了一些信息,原因是:以Integer类为例,它将缓存从-128到127的256个状态的Integer值,如果使用Integer.valueOf(int i),则传入int的范围恰好在在其中,将返回静态实例。

换句话说,如果您使用Integer.valueOf(),则可以直接返回静态实例。

可以看出,在日常编程中,可以使用Boolean.TRUE或FALSE时不要使用新的Boolean(),而可以使用Integer.valueOf时不要使用新的Integer()。其他数据类型也是如此。

如果传入对象的内部属性引用了其他对象,则sizeOf方法不会计算所引用对象的大小(有关详细信息,请参见deepSizeOf)。

3、deepSizeOf方法:

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

java分析内存占用的工具_java分析内存占用的工具

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

此类使用哈希表实现Map接口,并且在比较键(和值)时使用引用相等而不是对象相等。换句话说,在IdentityHashMap中,当且仅当(k1 == k 2)时,两个键k1和k2被视为相等。

然后是deepSizeOf的重载方法。这种方法有点长,并且会分节进行分析。

private static long deepSizeOf(Object o, Map 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方法以计算每个对象的大小。

java分析内存占用的工具_java分析内存占用的工具

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方法以累加大小

有一个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;
}

判断三种类型的属性:原始包装类型,静态类型和最终类型。后两个可以具有开关控制。首先是因为已经计算出sizeOf方法中的getObjectSize,所以这里无需重复计算。

到目前为止,还对代码的deepSizeOf部分进行了分析。

结论:

今天,SizeOf的源代码分析结束了,我希望它对那些了解它的人有所帮助。

关于开源工具SizeOf,这是我的另一个博客,欢迎提出更多建议。

使用SizeOf确定JVM中对象占用的内存:


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-342030-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...