MemoryCounter 본문

Programming/Java

MemoryCounter

halatha 2011. 4. 8. 01:05
//	http://www.javaspecialists.eu/archive/Issue078.html
import java.lang.reflect.*;
import java.util.*;
/**
 * This class can estimate how much memory an Object uses.  It is
 * fairly accurate for JDK 1.4.2.  It is based on the newsletter #29.
 */
public final class MemoryCounter {
	private static final MemorySizes sizes = new MemorySizes();
	private final Map visited = new IdentityHashMap();
	private final Stack stack = new Stack();

	public synchronized long estimate(Object obj) {
		assert visited.isEmpty();
		assert stack.isEmpty();
		long result = _estimate(obj);
		while (!stack.isEmpty()) {
			result += _estimate(stack.pop());
		}
		visited.clear();
		return result;
	}

	private boolean skipObject(Object obj) {
		if (obj instanceof String) {
			// this will not cause a memory leak since
			// unused interned Strings will be thrown away
			if (obj == ((String) obj).intern()) {
				return true;
			}
		}
		return (obj == null)
			|| visited.containsKey(obj);
	}

	private long _estimate(Object obj) {
		if (skipObject(obj)) return 0;
		visited.put(obj, null);
		long result = 0;
		Class clazz = obj.getClass();
		if (clazz.isArray()) {
			return _estimateArray(obj);
		}
		while (clazz != null) {
			Field[] fields = clazz.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				if (!Modifier.isStatic(fields[i].getModifiers())) {
					if (fields[i].getType().isPrimitive()) {
						result += sizes.getPrimitiveFieldSize(
								fields[i].getType());
					} else {
						result += sizes.getPointerSize();
						fields[i].setAccessible(true);
						try {
							Object toBeDone = fields[i].get(obj);
							if (toBeDone != null) {
								stack.add(toBeDone);
							}
						} catch (IllegalAccessException ex) { assert false; }
					}
				}
			}
			clazz = clazz.getSuperclass();
		}
		result += sizes.getClassSize();
		return roundUpToNearestEightBytes(result);
	}

	private long roundUpToNearestEightBytes(long result) {
		if ((result % 8) != 0) {
			result += 8 - (result % 8);
		}
		return result;
	}

	protected long _estimateArray(Object obj) {
		long result = 16;
		int length = Array.getLength(obj);
		if (length != 0) {
			Class arrayElementClazz = obj.getClass().getComponentType();
			if (arrayElementClazz.isPrimitive()) {
				result += length *
					sizes.getPrimitiveArrayElementSize(arrayElementClazz);
			} else {
				for (int i = 0; i < length; i++) {
					result += sizes.getPointerSize() +
						_estimate(Array.get(obj, i));
				}
			}
		}
		return result;
	}
}

import java.util.*;

public class MemorySizes {
	private final Map primitiveSizes = new IdentityHashMap() {
		{
			put(boolean.class, new Integer(1));
			put(byte.class, new Integer(1));
			put(char.class, new Integer(2));
			put(short.class, new Integer(2));
			put(int.class, new Integer(4));
			put(float.class, new Integer(4));
			put(double.class, new Integer(8));
			put(long.class, new Integer(8));
		}
	};
	public int getPrimitiveFieldSize(Class clazz) {
		return ((Integer) primitiveSizes.get(clazz)).intValue();
	}
	public int getPrimitiveArrayElementSize(Class clazz) {
		return getPrimitiveFieldSize(clazz);
	}
	public int getPointerSize() {
		return 4;
	}
	public int getClassSize() {
		return 8;
	}
}

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

public class TestMemoryUsage	{
	private static final long MEGABYTE = 1024L * 1024L;

	public static long bytesToMegabytes(long bytes) {
		return bytes / MEGABYTE;
	}

	public static void main(String[] args) {
		final int	SIZE	=	10000;
		int[][]	intArray	=	new int[SIZE][SIZE];
		for ( int i = 0; i < SIZE; ++i )
			for ( int j = 0; j < SIZE; ++j )
				intArray[i][j]	=	i * j;
		System.out.println("counted memory size =\t" + 4 * SIZE * SIZE);
		System.out.println("estimated memory size =\t" + new MemoryCounter().estimate(intArray));
		long[][]	longArray	=	new long[SIZE][SIZE];
		for ( int i = 0; i < SIZE; ++i )
			for ( int j = 0; j < SIZE; ++j )
				longArray[i][j]	=	i * j;
		System.out.println("counted memory size =\t" + 8 * SIZE * SIZE);
		System.out.println("estimated memory size =\t" + new MemoryCounter().estimate(longArray));
		List	intList	=	new ArrayList(SIZE);
		for ( int i = 0; i < SIZE; ++i )
			intList.add(i);
		System.out.println("estimated int list memory size =\t" + new MemoryCounter().estimate(intList));
		List	longList	=	new ArrayList(SIZE);
		for ( int i = 0; i < SIZE; ++i )
			longList.add((long)i);
		System.out.println("estimated long list memory size =\t" + new MemoryCounter().estimate(longList));
		Map	map	=	new HashMap();
		for ( int i = 0; i < SIZE; ++i )
			map.put(i, new String("value " + i));
		System.out.println("estimated hash map memory size =\t" + new MemoryCounter().estimate(map));
		long	estSize	=	0;
		for ( int i = 0; i < map.size(); ++i )
		{
			estSize	+= new MemoryCounter().estimate(new Integer(i)) + new MemoryCounter().estimate(new String("value " + i));
		}
		System.out.println("estimated hash map memory size =\t" + estSize);
	}
}
class 구조가 복잡해지면 정확도가 많이 떨어지기는 하지만 간단히 사용하기에 좋음
Comments