当前位置: 代码迷 >> Java相关 >> [讨论]由程序疑问2引发的 java的内存的使用的一些疑惑!
  详细解决方案

[讨论]由程序疑问2引发的 java的内存的使用的一些疑惑!

热度:488   发布时间:2007-10-11 11:29:30.0
[讨论]由程序疑问2引发的 java的内存的使用的一些疑惑!

来源:隔叶黄莺 - BlogJava

  在C/C++中有sizeof()操作,可轻易获知某个类型或实例占用内存大小,sizeof(int) 或者 sizeof(new TestClass)。可是Java中可没有这么直观的方法可用。

  因本人看过不少人写代码总爱写成

List userList = new ArrayList(); //注:声明时即初始化一个空 ArrayList
userList = userDao.getAllUsers(); //注:方法getAllUsers()中会生成一个ArrayList的

  上面就造成平白多了一个空的 ArrayList(),创建完后即刻就推向GC处置,我就在想这样一个空的 ArrayList 会占用多少内存,于是找来了 Optimizeit 观察后发现一个空的 ArrayList 要占去 24b 内存。那 Java 中有没有更便的捷的方法呢,于是在网上 google "java sizeof",引出不少话题。

  但我觉得比较好的一段代码是 java.sizeOf(http://sourceforge.net/projects/sizeof/),需要JDK1.5以上的版本支持,由它测定的空 ArrayList 所占内存确也是 24b。下载到 SizeOf_0_2_src.zip(其中含SizeOf.jar,其实就一个类 SizeOf),假设解压到 F:\Component Library\java\javasizeof。

  用法很简单,直接看它的 README.txt 文件就行,因为只有一个类,方法也不多,全是静态方法,可以使用静态引入,看看就明白,只是现在的 0.2 版本推荐的方法是 sizeOf() 和 deepSizeOf(),而不再是 iterativeSizeOf() 的。

  它还提供了一个测试代码 TestSizeOf(可从中学习 SizeOf 的用法),在命令行下,进入到 F:\Component Library\java\javasizeof目录,然后执行
F:\Component Library\java\javasizeof>java -javaagent:SizeOf.jar net.sourceforge.sizeof.test.TestSizeOf

显示结果是:
JAVAGENT: call premain instrumentation for class SizeOf
Starting test...
simple obj: 40.0b
int: 0.0b
long: 0.0b
char: 0.0b
double: 16.0b
boolean: 0.0b
Integer: 0.0b
empty string: 0.0b
not empty string: 0.0b
not empty string: 0.0b
simple obj: 24.0b
simple obj: 40.0b
empty list: 24.0b
10 list: 24.0b
20 list no static: 24.0b
1000 o arr: 816.0b

  应该会惊讶一下,为什么会出现那么多 0.0b?为什么要用1.5以上版本的jdk,为什么不是用 -classpath 参数,而是 -javaagent 参数?

  我们不妨把 javasizeof 的src目录中源代码导入到 eclipse中(相信大多数人都用这个的),可以看到源代码 TestSizeOf,把 SizeOf.skipFlyweightObject(true) 行注释掉,运行 TestSizeOf。

  不小心的话,你应该收到 java.lang.IllegalStateException: Instrumentation is null 的异常,没错这个 SizeOf 用到了 JDK1.5 后新加入的 java.lang.instrument.Instrumentation 接口,所以您需要为 TestSizeOf 设置 VM arguments
-javaagent:"F:\Component Library\java\javasizeof\SizeOf.jar"

  (注意到在 SizeOf.jar包中的 META-INF\MANIFEST.MF中有 Premain-Class: net.sourceforge.sizeof.SizeOf,这就是 -javaagent: 所在意的。指向SizeOf.jar的路径中有空格的话一定要用双引号引起来)

  运行后结果就是:

JAVAGENT: call premain instrumentation for class SizeOf
Starting test...
simple obj: 40.0b
int: 16.0b
long: 16.0b
char: 16.0b
double: 16.0b
boolean: 16.0b
Integer: 16.0b
empty string: 24.0b
not empty string: 24.0b
not empty string: 24.0b
simple obj: 24.0b
simple obj: 40.0b
empty list: 24.0b
10 list: 24.0b
20 list no static: 24.0b
1000 o arr: 816.0b

  对于现在的输出值细细去体会吧,也许不符合您早已形成的较为稳固的想法,或许你发现确实存在出入。

  SizeOf_0_2_src.zip包中的 README.txt 中部分内容:

java.SizeOf is a simple java agent what can be used to calculate the memory size
of java objects.
The agent is implemented with the java.lang.instrument introduced with java 5.
Here is a simple howto:

1) use the class SizeOf in your code to test the size of your java object:

//configuration steps
SizeOf.skipStaticField(true);
SizeOf.setMinSizeToLog(10);

//calculate object size
SizeOf.iterativeSizeOf()

2) start the jvm with the argument: -javaagent:/SizeOf.jar

To avoid the dependencies of your code to SizeOf the best use of the agent is in
conjunction with aspect.

搜索更多相关的解决方案: 内存  java  疑问  ArrayList  

----------------解决方案--------------------------------------------------------
JAVAGENT: call premain instrumentation for class SizeOf
Starting test...
simple obj: 24.0b
simple obj deepSizeOf: 776.0b
int: 16.0b
long: 16.0b
char: 16.0b
double sizeOf: 16.0b
double deepSizeOf: 176.0b
boolean: 16.0b
Integer: 16.0b
Integer: deepSizeOf 752.0b
empty string: 24.0b
not empty string: 24.0b
not empty string: 24.0b
not empty string deepSizeOf: 80.0b
simple obj: 24.0b
simple obj deepSizeOf: 776.0b
empty list: 24.0b
10 list: 24.0b
20 list no static: 24.0b
1000 o arr deepSizeOf: 816.0b
----------------解决方案--------------------------------------------------------

我觉得那篇文章是极不负责任的,文章作者没有深入分析sizeOf就顽下结论呢
经过测试结果发现和预期的是一致的结果

package net.sourceforge.sizeof.test;
import static net.sourceforge.sizeof.SizeOf.humanReadable;
import static net.sourceforge.sizeof.SizeOf.deepSizeOf;
import static net.sourceforge.sizeof.SizeOf.sizeOf;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import net.sourceforge.sizeof.SizeOf;


public class TestSizeOf
{
/**
* A simple class to test SizeOf behavior
* @throws FileNotFoundException
*/
public static void main(String[] args) throws FileNotFoundException
{
print("Starting test...");

//SizeOf.turnOnDebug();
SizeOf.skipStaticField(false);
SizeOf.skipFinalField(false);
SizeOf.skipFlyweightObject(false);
//SizeOf.setMinSizeToLog(10);
//SizeOf.setLogOutputStream(new FileOutputStream("log.txt"));

Integer x =1;
int intV = 0;
long _long = 0;
Dummy dummy = new Dummy();
String s;

ArrayList<Integer> aList = new ArrayList<Integer>();

Object[] oArray = new Object[200];

//print("simple obj: \t", sizeOf(oArray));
dummy.dummy=dummy;
dummy.dummy2=dummy;
print("simple obj: \t", sizeOf(dummy));
print("simple obj deepSizeOf: \t", deepSizeOf(dummy));

//print("int: \t\t", iterativeSizeOf(_int));
print("int: \t\t", sizeOf(intV));
print("int deepSizeOf: \t\t", deepSizeOf(intV));
print("long: \t\t",sizeOf(_long));
print("long deepSizeOf: \t\t",deepSizeOf(_long));
print("char: \t\t",sizeOf('a'));
print("char deepSizeOf: \t\t",deepSizeOf('a'));
print("double sizeOf: \t\t",sizeOf(0.3D));
print("double deepSizeOf: \t\t",deepSizeOf(0.3D));
print("boolean: \t\t",sizeOf(true));
print("Integer: \t\t",sizeOf(Integer.valueOf(2)));
print("Integer: deepSizeOf \t\t",deepSizeOf(Integer.valueOf(2)));

print("empty string: \t", sizeOf(""));
print("not empty string: \t", sizeOf("aaaa"));
print("not empty string deepSizeOf: \t", deepSizeOf("aaaa"));
print("not empty string: \t", sizeOf("aaaaaaaa"));
print("not empty string deepSizeOf: \t", deepSizeOf("aaaaaaaa"));
//print("empty string: \t", iterativeSizeOf(""));
print("simple obj: \t", sizeOf(dummy));
print("simple obj deepSizeOf: \t", deepSizeOf(dummy));
//print("empty array: \t", sizeOf(aList));
print("empty list: \t", sizeOf(aList));
print("empty list deepSizeOf: \t", deepSizeOf(aList));

for (int i = 1; i < 10; ++i)
aList.add(i);

//print("10 list: \t", sizeOf(aList));
print("10 list: \t", sizeOf(aList));
for (int i = 11; i < 20; ++i)
aList.add(i);
print("20 list no static: \t", sizeOf(aList));
print("20 list no static deepSizeOf: \t", deepSizeOf(aList));
print("1000 o arr: \t", sizeOf(oArray));
print("1000 o arr deepSizeOf: \t", deepSizeOf(oArray));
}

public static void print(String msg, long n)
{

print(msg + humanReadable(n));
}

public static void print(String msg)
{
System.out.println(msg);
}

public static void heinz() throws IllegalArgumentException, IllegalAccessException, IOException {
measureSize(new Object());
measureSize(new HashMap());
measureSize(new LinkedHashMap());
measureSize(new ReentrantReadWriteLock());
measureSize(new byte[1000]);
measureSize(new boolean[1000]);
measureSize(new String("Hello World".toCharArray()));
measureSize("Hello World");
measureSize(10);
measureSize(100);
measureSize(1000);
measureSize(new Parent());
measureSize(new Kid());
measureSize(Thread.State.TERMINATED);
measureSize(Boolean.TRUE);
}

private static class Parent {
private int i;
private boolean b;
private long l;
}

private static class Kid extends Parent {
private boolean b;
private float f;
}

private static void measureSize(Object object)
{
print(object.getClass().getSimpleName() +", shallow="+ sizeOf(object)+ ", deep="+deepSizeOf(object));
}
}


测试结果
JAVAGENT: call premain instrumentation for class SizeOf
Starting test...
simple obj: 24.0b
simple obj deepSizeOf: 776.0b
int: 16.0b
int deepSizeOf: 752.0b
long: 16.0b
long deepSizeOf: 176.0b
char: 16.0b
char deepSizeOf: 176.0b
double sizeOf: 16.0b
double deepSizeOf: 176.0b
boolean: 16.0b
Integer: 16.0b
Integer: deepSizeOf 752.0b
empty string: 24.0b
not empty string: 24.0b
not empty string deepSizeOf: 72.0b
not empty string: 24.0b
not empty string deepSizeOf: 80.0b
simple obj: 24.0b
simple obj deepSizeOf: 776.0b
empty list: 24.0b
empty list deepSizeOf: 80.0b
10 list: 24.0b
20 list no static: 24.0b
20 list no static deepSizeOf: 1.1328125Kb
1000 o arr: 816.0b
1000 o arr deepSizeOf: 816.0b


----------------解决方案--------------------------------------------------------

对于值变量,如int ,char double,...结果16b
而对于引用对象的都是24b

呵呵呵c++,c里面的机制也没有多少差别阿

唯一超过预期的是
print("1000 o arr: \t", sizeOf(oArray)); 竟然输出了816.0b而不是期望的24.0b

int deepSizeOf: 752.0b



----------------解决方案--------------------------------------------------------
郁闷阿,忘记了一个基本的东西了任何值对象在jdk1.5中会发生自动装箱操作
sizeOf调用了getObjectSize 这个是一个native方法
----------------解决方案--------------------------------------------------------
在c和c++中里面的sizeOf其实是无法和我们定义的类型严格匹配的,就是
class A{
int i;
char c;
}
当sizeof一个变量的大小的时候不是简单的int+char的内存大小
因为存在内存间隙,这个市编译器编译时产生的
----------------解决方案--------------------------------------------------------
以下是引用时空之蕊在2007-10-11 11:54:24的发言:
在c和c++中里面的sizeOf其实是无法和我们定义的类型严格匹配的,就是
class A{
int i;
char c;
}
当sizeof一个变量的大小的时候不是简单的int+char的内存大小
因为存在内存间隙,这个市编译器编译时产生的

编译器有个编译开关,把它关上就可以得到预期的大小了.
在32位的机器上,默认情况下会向32位对齐.
----------------解决方案--------------------------------------------------------

以下是引用Eastsun在2007-10-11 12:34:08的发言:

编译器有个编译开关,把它关上就可以得到预期的大小了.
在32位的机器上,默认情况下会向32位对齐.

这个间隙问题不是编译器的问题,那本c++深度遍历里面讲的间隙产生的原因是寻址和内存地址分配原则所导致的问题
寻址的时候的偏移实际上至少是一个偶数,
这个与开关没有关系的吧


----------------解决方案--------------------------------------------------------
以下是引用时空之蕊在2007-10-13 14:45:37的发言:

这个间隙问题不是编译器的问题,那本c++深度遍历里面讲的间隙产生的原因是寻址和内存地址分配原则所导致的问题
寻址的时候的偏移实际上至少是一个偶数,
这个与开关没有关系的吧

楼上不要想当然...
还是用事实说话吧:

程序代码:

#include<iostream>

struct P{
char a;
short b;
int c;
};
int main(){
std::cout<<\"sizeof(char) :\"<<sizeof(char)<<std::endl;
std::cout<<\"sizeof(short) :\"<<sizeof(short)<<std::endl;
std::cout<<\"sizeof(int) :\"<<sizeof(int)<<std::endl;
std::cout<<\"sizeof(P) :\"<<sizeof(P)<<std::endl;
}


这个代码的输出是:

sizeof(char) :1
sizeof(short) :2
sizeof(int) :4
sizeof(P) :8


程序代码:

#include<iostream>
#pragma pack(1) //编译开关,按一字节对齐,也就是不对齐

struct P{
char a;
short b;
int c;
};
int main(){
std::cout<<\"sizeof(char) :\"<<sizeof(char)<<std::endl;
std::cout<<\"sizeof(short) :\"<<sizeof(short)<<std::endl;
std::cout<<\"sizeof(int) :\"<<sizeof(int)<<std::endl;
std::cout<<\"sizeof(P) :\"<<sizeof(P)<<std::endl;
}


输出:

sizeof(char) :1
sizeof(short) :2
sizeof(int) :4
sizeof(P) :7


----------------解决方案--------------------------------------------------------

领教了,刚才验证了下,确实是楼上说的!

谢谢!

When you use #pragma pack(n), where n is 1, 2, 4, 8, or 16, each structure member after the first is stored on the smaller member type or n-byte boundaries.

受教

[此贴子已经被作者于2007-10-13 23:18:19编辑过]


----------------解决方案--------------------------------------------------------
  相关解决方案