这个问题底部的代码有点长,但基本上会创建一些对象并确定它们在内存中的大小.我使用以下JVM参数执行代码(TLAB以避免块内存分配并据称获得准确的内存使用数字):
-server -Xms2000m -Xmx2000m -verbose:gc -XX:-UseTLAB
Run Code Online (Sandbox Code Playgroud)
我在64位Hotspot JVM上运行代码并获得以下输出:
Java HotSpot(TM)64位服务器VM
对象:16个字节具有1 int的对象:16字节
具有2个int的
对象:24字节具有3个int的对象:24个字节具有1个
long的对象:24个字节具有2个long的
对象:32个字节具有3个long的对象:40个字节具有1个引用的对象:16个字节
具有2个引用的
对象:24个字节具有3个引用的对象:24个字节
我的结论是:
但我很难理解为什么引用不会像longs 一样使用空间.
由于64位JVM上的引用是8个字节,因此明显的结论是测量方法存在缺陷*.你能解释一下发生了什么以及可以采取哪些措施来解决这个问题?
*注意:
- 测量期间没有GC运行.
- 使用Netbeans分析器产生类似的结果.
public class TestMemoryReference {
private static final int SIZE = 100_000;
private static Runnable r;
private static Object o = new Object();
private static Object o1 = new Object();
private static Object o2 = new Object();
private static Object o3 = new Object();
public static class ObjectWith1Int { int i; }
public static class ObjectWith2Ints { int i, j; }
public static class ObjectWith3Ints { int i, j, k; }
public static class ObjectWith1Long { long i; }
public static class ObjectWith2Longs { long i, j; }
public static class ObjectWith3Longs { long i, j, k; }
public static class ObjectWith1Object { Object o = o1; }
public static class ObjectWith2Objects { Object o = o1; Object p = o2; }
public static class ObjectWith3Objects { Object o = o1; Object p = o2; Object q = o3; }
private static void test(Runnable r, String name, int numberOfObjects) {
long mem = Runtime.getRuntime().freeMemory();
r.run();
System.out.println(name + ":" + (mem - Runtime.getRuntime().freeMemory()) / numberOfObjects + " bytes ");
}
public static void main(String[] args) throws Exception {
System.out.println(System.getProperty("java.vm.name") + " ");
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new Object(); } };
test(r, "Object", SIZE);
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Int(); } };
test(r, "Object with 1 int", SIZE);
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Ints(); } };
test(r, "Object with 2 ints", SIZE);
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Ints(); } };
test(r, "Object with 3 ints", SIZE);
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Long(); } };
test(r, "Object with 1 long", SIZE);
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Longs(); } };
test(r, "Object with 2 longs", SIZE);
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Longs(); } };
test(r, "Object with 3 longs", SIZE);
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith1Object(); } };
test(r, "Object with 1 reference", SIZE);
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith2Objects(); } };
test(r, "Object with 2 references", SIZE);
r = new Runnable() { public void run() { for (int i = 0; i < SIZE; i++) o = new ObjectWith3Objects(); } };
test(r, "Object with 3 references", SIZE);
}
}
Run Code Online (Sandbox Code Playgroud)
因为64位JVM上的引用是8个字节
这是你可能有缺陷的假设.
HotSpot能够使用"压缩的oops"在JVM的某些地方使用32位值作为引用(强调我的):
哪些oops被压缩了?
在ILP32模式JVM中,或者如果在LP64模式下关闭UseCompressedOops标志,则所有oops都是本机机器字大小.
如果UseCompressedOops为true,则将压缩堆中的以下oops:
- 每个对象的klass字段
- 每个oop实例字段
- oop数组的每个元素(objArray)
我怀疑这是你的情况.
使用测试它
-XX:-UseCompressedOops
Run Code Online (Sandbox Code Playgroud)
要么
-XX:+UseCompressedOops
Run Code Online (Sandbox Code Playgroud)
在我的机器上,默认情况下我得到的结果与你相同,但-XX:-UseCompressedOops我看到:
Object:16 bytes
Object with 1 int:24 bytes
Object with 2 ints:24 bytes
Object with 3 ints:32 bytes
Object with 1 long:24 bytes
Object with 2 longs:32 bytes
Object with 3 longs:40 bytes
Object with 1 reference:24 bytes
Object with 2 references:32 bytes
Object with 3 references:40 bytes
Run Code Online (Sandbox Code Playgroud)
...这可能更接近你的期望:)