Java类与数组内存大小的区别?

10

我需要在Java程序中存储数百万个X/Y双精度对以供参考。我希望尽可能地降低内存消耗和对象引用的数量。所以经过一些思考,我决定将两个点保存在一个小的双精度数组中,其设置如下:

double[] node = new double[2];
node[0] = x;
node[1] = y;

我认为使用数组将会防止类与我的在类中使用的X和Y变量之间的联系,代码如下:

class Node {
     public double x, y;
}

在我阅读了关于类中公共字段存储方式的资料后,我想到字段可能并不是像指针一样的结构。也许JVM只是将这些值存储在连续的内存中,并且知道如何在没有地址的情况下找到它们,因此我的点的类表示形式比数组小。

所以问题是,哪个具有更小的内存占用?为什么?

我特别想知道类字段是否使用指针,因此是否具有32位的开销。

3个回答

5
后者的占用空间更小。
基本类型在包含类中内联存储。因此,您的Node需要一个对象头和两个64位插槽。您指定的数组使用一个数组头(>=一个对象头)加上两个64位插槽。
如果您要以这种方式分配100个变量,则并不重要,因为只是头大小不同。
注意:由于您未指定JVM,因此所有这些都有些推测性,某些细节可能因JVM而异。

目前在 Mac OS X 上以 x64 运行 Java 1.7 SE。现在我假设一个巨大的二维数组将完全消除每个节点所需的对象引用,因此这将是迄今为止最节省内存的方法? - Cody Smith
如果您所说的2D数组是指double[2][n]或double[n][2],那么这将导致对象引用。因为在Java中,这种情况下的数组实际上是数组的数组。 - Markus Mikkolainen
(你过度优化了,但是...)最小的内存占用应该是使用一维数组并显式计算索引。在Java中,二维数组只是指向一维数组的指针数组。 - Keith Randall
我建议您创建一个具有长ID、双精度X和Y的节点,并通过ID进行比较,当您从数据库中读取它们时将它们插入到TreeSet中。 - Markus Mikkolainen
treeset会给你log(n),但在这种情况下应该足够了,因为n仍然相对较小且操作便宜。 - Markus Mikkolainen
既然一维数组似乎是正确的选择,那么如果我创建三个一维数组呢?其中一个是长整型(用于ID),另外两个是双精度浮点数(用于X和Y)。我的数据库可以使用“ORDER BY” SQL语句将数据按顺序带入我的应用程序,因此数据已经为我排序。这将允许我编写二分搜索算法,利用ID数组进行搜索,一旦找到节点,该索引就可以在其他两个数组上使用,以获取X和Y坐标。所有这些只需要三个对象引用,听起来怎么样? - Cody Smith

0

我认为你最大的问题不是存储数据,而是检索、索引和操作它。

然而,从根本上讲,数组是解决问题的方法。如果你想节省指针,可以使用一维数组。(有人已经提到了这一点)。


0

首先,必须声明实际空间使用取决于您使用的JVM。这是严格的实现特定的。以下是针对典型主流JVM的情况。

那么问题是,哪个具有更小的内存占用?为什么?

第二个版本更小。数组具有对象头中保存数组长度的32位字段的开销。对于非数组对象,大小在类中是隐含的,不需要单独表示。

但请注意,这是每个数组对象的固定开销。数组越大,实际上开销就越不重要。而使用类而不是数组的反面是索引无法工作,因此您的代码可能会更复杂(并且更慢)。

一个Java 2D数组实际上是由一维数组构成的数组(等等),因此您可以对更高维度的数组应用相同的分析。任何维度中一个数组的大小越大,开销的影响就越小。在2x10数组中的开销将比在10x2数组中的开销要小。(好好想一想……1个长度为2的数组+2个长度为10的数组1个长度为10的数组+10个长度为2的数组相比,开销与数组数量成正比。)

我特别想知道类字段是否使用指针,因此是否具有32位的开销。

(实际上你在谈论实例字段而非类字段。这些字段不是static的…)

类型为原始类型的字段直接存储在对象的堆节点中,没有任何引用。在这种情况下,没有指针开销。

然而,如果字段类型是包装类型(例如Double而不是double),那么可能会有引用开销和Double对象的对象头开销。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接