在某些情况下,需要高效的内存来存储许多对象。要在Java中做到这一点,您不得不使用多个原始数组(请参阅下面的原因)或一个大字节数组,这会产生一点CPU开销进行转换。
示例:您有一个class Point { float x; float y;}
。现在,您想将N个点存储在一个数组中,该数组在32位JVM上至少需要N * 8个字节的浮点数和N * 4个字节的引用。因此,至少有1/3是垃圾(此处未计入正常对象开销)。但是,如果将其存储在两个float数组中,一切都会好的。
我的问题:为什么Java不能优化引用数组的内存使用?我的意思是为什么不像在C ++中那样直接将对象嵌入到数组中?
例如。将Point类标记为final足以使JVM看到Point类的最大数据长度。还是这对the specification不利?同样,在处理大型n维矩阵等时,这将节省大量内存
更新:
我想知道JVM在理论上是否可以优化它(例如在后台)以及在什么条件下-我是否可以以某种方式强制JVM。我认为结论的第二点是为什么根本不容易做到这一点的原因。
结论JVM需要知道的内容:
该类必须是最终类,以使JVM猜测一个数组条目的长度
该阵列必须是只读的。当然,您可以更改Point p = arr[i]; p.setX(i)
之类的值,但不能通过inlineArr[i] = new Point()
写入数组。否则,JVM将不得不引入与“ Java方式”相反的复制语义。见阿罗斯的答案
如何初始化数组(调用默认构造函数或将成员初始化为默认值)
最佳答案
您描述的场景可能会节省内存(尽管实际上我不确定它是否会这样做),但是当实际将对象放入数组时,可能会增加相当多的计算开销。考虑到当您执行new Point()
时,您创建的对象是动态分配在堆上的。因此,如果通过调用Point
分配100个new Point()
实例,则不能保证它们的位置在内存中是连续的(实际上,它们很可能不会分配给连续的内存块)。
那么Point
实例实际上如何将其放入“压缩”数组中?在我看来,Java必须将Point
中的每个字段显式复制到为该数组分配的连续内存块中。对于具有许多字段的对象类型,这可能会变得昂贵。不仅如此,原始的Point
实例仍在堆上以及数组内部占用空间。因此,除非立即进行垃圾收集(我想可以重写任何引用以指向放置在数组中的副本,从而从理论上允许立即对原始实例进行垃圾收集),否则实际上您将使用比您更多的存储空间如果您刚刚将引用存储在数组中。
此外,如果您有多个“压缩”数组和可变对象类型怎么办?将对象插入数组必然会将对象的字段复制到数组中。因此,如果您执行以下操作:
Point p = new Point(0, 0);
Point[] compressedA = {p}; //assuming 'p' is "optimally" stored as {0,0}
Point[] compressedB = {p}; //assuming 'p' is "optimally" stored as {0,0}
compressedA[0].setX(5)
compressedB[0].setX(1)
System.out.println(p.x);
System.out.println(compressedA[0].x);
System.out.println(compressedB[0].x);
...您将获得:
0
5
1
...即使在逻辑上也应该只有
Point
的一个实例。存储引用避免了这种问题,并且还意味着在任何情况下,如果在多个阵列之间共享一个非平凡的对象,您的总存储使用量可能会比每个阵列存储该对象的所有字段的副本时的总使用量要低。