【问题标题】:Java 64 bit uses more memory than a 32 bit versionJava 64 位比 32 位版本使用更多内存
【发布时间】:2016-05-28 05:50:50
【问题描述】:

我正在测试我的 java 应用程序,我注意到 Java 64 位版本比在 Java 32 位版本中执行应用程序使用的更多。

我测试的服务器是 Windows 7-64 位和 Solaris-64 位,但在这两种情况下都发生了相同的行为。顺便说一句,应用程序使用默认的 VM 参数运行,使用的 Java 版本是 8u65s。

因为我的服务器是 64 位的,所以正确的选择应该是 Java 64 位,但是有什么理由吗?在什么情况下 32 位版本比 64 位版本好?

两者都分配的内存:

32-bit : 74mb
64-bit: 249mb

【问题讨论】:

  • 您能否提供更多信息:您正在使用什么 JVM、您正在运行什么代码、您提供了哪些 JVM 标志,以及这种内存使用是即时的还是随时间推移的?仅凭提供的信息,我不确定我们能否很好地回答您的问题。
  • 旁注:架构名称是 32/64 bit,而不是 bits
  • Java版本为8u65,两种情况下均未设置VM参数。我正在执行一个 Spring 应用程序,但其他应用程序也遇到了同样的行为。我通过 Windows 上的系统管理器看到了内存使用情况。
  • 从以前的帖子中看到这个答案:stackoverflow.com/a/4408987/3299157
  • 不要从外部看你的 JVM,使用从内部显示内存消耗的工具(VisualVM,...)

标签: java performance memory 32bit-64bit


【解决方案1】:

64位内存模型占用更多内存是正确的。

除此之外,我只是想提一个 Solaris 陷阱,所以这并不是您问题的完整答案,但下面的答案可以完全解释您所看到的 74mb 和 249mb 之间的差异。

Solaris 不再有 32 位版本的 Java 是正确的,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

在没有更多命令行选项的 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

在没有更多命令行选项的 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。从那时起发生了很多事情。

【讨论】:

    【解决方案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。

    【讨论】:

    • 另外,64 位与 32 位是运行时在决定是在服务器模式还是客户端模式下运行时考虑的因素之一,而服务器模式从较高的初始内存分配开始。
    • 是的,64 位 Java 需要更多内存,主要是因为指针更大。但这并不能解释 3 倍的增长。我建议做堆转储来比较内存的使用位置
    • @kohlerm。 3x 更改可以通过更改 -Xms 的默认值来解释。看我的回答here
    猜你喜欢
    • 2012-01-10
    • 2021-07-08
    • 2020-10-28
    • 1970-01-01
    • 1970-01-01
    • 2011-12-04
    • 2010-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多