我正在测试我的java应用程序,我注意到Java 64位版本比在Java 32位版本中执行应用程序要多得多。
我测试的服务器是Windows 7-64位和Solaris-64位,但在这两种情况下都发生了相同的行为。顺便说一下,应用程序运行默认的VM参数,使用的Java版本是8u65s。
由于我的服务器是64位,正确的选择将采用Java 64位,但这有什么理由吗?在什么情况下32位版本比64位好?
两者分配的内存:
32-bit : 74mb
64-bit: 249mb
答案 0 :(得分:2)
这是Java(以及Microsoft .NET)的正常行为,主要是因为它们的指针模型以及它们的垃圾收集模型。
对象变量实际上是指向堆上对象的指针。在64位版本中,此指针需要两倍的空间。因此,存储在容器中的指针将需要更多内存,垃圾收集器保留的指针也将需要收集。由于对象主要由指向其他对象的指针组成,因此32位和64位之间的差异非常快。
除此之外,垃圾收集器必须有效地跟踪所有这些对象,并且在64位版本中,收集器倾向于使用更大的最小分配大小,因此它不必跟踪尽可能多的记忆片。这是有道理的,因为无论如何物体都更大。我认为最小大小在32位模式下通常为16字节,在64位模式下为32字节,但这些大小完全取决于您使用的特定虚拟机,因此它们会有所不同。
例如,如果您的对象只需要12个字节的堆内存,并且您在最小分配大小为32字节的虚拟机上运行,则它将使用32个字节,其中20个字节被浪费。如果在最小大小为16字节的机器上分配相同的对象,则它将使用16个字节,浪费4个字节。替代方法是浪费更多内存来跟踪这些块,因此这实际上是最好的方法,并将保持应用程序的性能和资源利用率平衡。
要记住的另一件事是Java运行时从操作系统为其堆分配内存块,然后程序可以从这些块中分配内存。运行时试图保持领先于程序的内存需求,因此它将分配超出需要的数量,并让您的程序进入它。使用更高的最小分配大小,64位运行时将为其堆分配更大的块,因此您将拥有比32位运行时更多的未使用内存。
如果内存消耗是特定应用程序的严重限制,您可以考虑使用本机编译的C ++(使用实际的C ++标准,而不是带有指向对象的指针的旧版C!)。原生C ++通常需要1/5的Java内存来完成同样的事情,这就是本机代码在移动设备上更受欢迎的原因(C ++和Objective C)。当然,C ++也有自己的问题,因此除非你迫切需要减少内存消耗,否则最好将其视为正常行为,并继续使用64位Java。
答案 1 :(得分:2)
64位内存模型占用更多内存是正确的。
除此之外我只是想提一下Solaris问题,所以这不是你问题的完整答案,但下面的答案可以完全解释你所看到的从74mb到249mb的差异。
正确的是,不再是32位版本的Java for Solaris,Mac OS X的情况也是如此。请注意,对于Solaris上的Java 7,您总是得到32位Java(即使你已经安装了64位Java),除非你明确地请求带有-d64
标志的64位。所以一定不要在这里比较苹果和橘子。很多人在Solaris 上认为他们一直在运行64位Java,因为他们安装了它,并不知道它必须明确要求。
对于Solaris上的Java 8,指定-dXX
没有意义,因为那里只有64位版本。
因此 - 简单地说就是这样 - 内存设置的默认值已经改变。我所说的是仅仅因为这个(而不是关于内存指针的讨论),看起来你的Solaris上的Java 8正在从操作系统中占用更多内存。这实际上是海市蜃楼。
以下是16 GB系统的回顾(值会根据您安装的RAM数量而变化):
在没有其他命令行选项的Solaris上使用Java 7,您将获得32位内存模型(即使安装了64位版本也隐含-d32
),默认值如下:
memory model: 32 bit
-Xms default : 64 MB
-Xmx default : 1 GB
如果你明确使用-d64
,你会得到:
memory model: 64 bit
-Xms default : 256 MB
-Xmx default : 4 GB
在没有进一步命令行选项的Solaris上使用Java 8,您将获得64位内存模型(隐含-d64
,-d32
现在是非法的),默认值如下:
memory model: 64 bit
-Xms default : 256 MB
-Xmx default : 4 GB
至于您的评论,您已经读过:"当您转移到64位VM时,SPARC大约会降低10-20%"。我对此表示怀疑。我可以看到你已经阅读了它here但该文档适用于Java 1.4以及可能的Java 5.从那以后发生了很多事情。