一个简单的"Hello World"在64位机器上需要10G虚拟内存而在32位机器上需要1G?

use*_*431 66 java 64-bit jvm memory-management virtual-memory

在我们的生产机器上运行一个简单的Java程序,我注意到这个程序占用了更多的10G virt.我知道虚拟内存不是那么相关,但至少我想了解为什么需要它.

public class Main {
  public static void main(String[] args) {
        System.out.println("Hello World!");
        try {
                Thread.sleep(10000);
        } catch(InterruptedException e) {
                /* ignored */
        }
  }
}
Run Code Online (Sandbox Code Playgroud)

top当我运行那个小程序时,这是什么意思:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
18764 myuser    20   0 10.2g  20m 8128 S  1.7  0.1   0:00.05 java
Run Code Online (Sandbox Code Playgroud)

有谁知道为什么会这样?

uname -a说:

Linux m4fxhpsrm1dg 2.6.32-358.18.1.el6.x86_64 #1 SMP Fri Aug 2 17:04:38 EDT 2013 x86_64 x86_64 x86_64 GNU/Linux
Run Code Online (Sandbox Code Playgroud)

在较旧的32bit-linux机器上,同一程序仅消耗大约1G的virt.旧机器有4GB RAM,新机器有32GB.

Mic*_*rdt 86

初始堆和最大堆默认大小定义为机器物理内存的百分比,现在生产服务器往往有很多.

您可以通过-Xms和-Xmx命令行选项进行选择.

  • 等待.*>最大堆大小:较小的物理内存的1/4或1GB* - 因此默认的最大堆上限为1 GB.假设这没有改变,你是说初始分配高于*最大*?因为这听起来很破碎. (6认同)
  • @Bob似乎这仅适用于客户端VM上的32位Java和1.6之前的64位.服务器VM上的Java 1.6+ 64位开心地保留了更多. (4认同)
  • @Bob:"注意:为Java SE 5.0提供的堆大小的边界和分数是正确的.随着计算机变得更强大,它们在后续版本中可能会有所不同." (2认同)

Lua*_*aan 46

虚拟内存对你来说无关紧要.

32位和64位之间的基本区别在于64位的地址空间非常大.如果10 GiB看起来很多,请注意64位上的.NET可以像这样使用内存的TiB.然而在32位上,.NET更加保守(JVM也是如此) - 地址空间总共为4 GiB - 这并不是很多.

但这无关紧要 - 没关系.这只是一个大大简化编程的事情,并且对主机操作系统没有任何负面影响.它为VM创建了一个连续的地址空间,这意味着你不必分割堆(或者更糟的是,堆栈,它或多或少是不可能的 - 但那些往往只是MiB左右)如你需要更多的"真实"记忆.当你最终提交虚拟内存时,它会变得更加真实 - 在这一点上,它或多或少必须由某些数据存储支持- 无论是页面(交换)文件还是物理RAM.

关键是,内存的物理位置不一定是连续的,但这是在您的范围之外完成的,并且映射通常非常快.另一方面,必须索引一个数组,实际上是在10个不同的虚拟地址内存块上进行分段,这是(完全不必要的)工作.

所以你有它 - 虚拟内存在64位几乎是免费的.基本方法是"如果它在那里,使用它".你不是限制其他应用程序,它可以节省你相当多的工作,如果你最后实际使用它.但在此之前,您只能预订.它根本不会转换为任何物理内存.你不支付今晚可能会来的朋友并坐在你的餐桌旁,但如果他们确实来了,你仍然有空间让他们坐下 - 只有当他们最终来的时候,你才真正得到"收费".

有关Java在不同计算机上以及不同版本上的行为方式的详细信息,请参阅此问题:来自Java SE 6的Sun JVM的默认最大堆大小是多少? 最大堆大小也决定了保留的虚拟内存量,因为堆必须是连续的地址空间.如果它没有预先保留,则可能发生堆无法扩展到此最大值,因为其他人在堆必须扩展的位置保留了一个地址空间区域.

  • @gnat它通过解释问题的前提(虚拟地址空间的10G在某种程度上是坏的或不合需要的)是错误的,并且有一个好处(防止碎片). (12认同)
  • @gnat怎么样?`它创建了一个连续的地址空间供VM使用,这意味着你不必分割堆`对我来说似乎是一个非常明确的理由,等等.我已经解释了32位和64位环境之间的区别. (9认同)

Pie*_*r B 7

这不是你的程序耗尽内存,而是Java VM保留内存,无论加载哪个程序.

  • 为什么这个错误的答案有这么多的赞成.Java VM不**"使用"那么多内存.它只分配了那么多的虚拟地址空间.这与"使用"多少"记忆"无关. (7认同)
  • 它仍然不正确.它不是"保留"任何记忆.它呈现了一定大小的虚拟地址空间.此时内存不是"保留"或"已分配".它确实是"虚拟"地址空间.它并没有真正反映实际的记忆. (5认同)
  • 不,它说它**可以**使用它,而不是(还)使用它. (3认同)

tho*_*ter 7

事实证明,在使用虚拟内存寻址的现代计算机体系结构中(应用程序看到的"内存空间"实际上与实际物理分配的内存无关),这个虚拟"内存空间"的数量无关紧要"在启动时给予申请.这并不意味着系统已分配了这么多内存.

如果一个应用程序看到10GB的虚拟地址空间大,它向应用程序发出的信号是,如果需要,它可以使用高达10GB的内存地址.但是,在实际写入内存之前,内存实际上并未在物理RAM中分配,这是在逐页的基础上完成的,其中页面是4kB的内存部分.虚拟地址空间就是这样 - 在实际使用之前完全虚拟化.

假设一个应用程序有10GB的地址空间,它开始使用其中一些.作为一个"新鲜" - 以前未触及 - 首先写入此虚拟内存的页面,系统将在低级别上将此虚拟页面"映射"到一部分物理内存,然后编写它.但是,该应用程序本身不必担心这些细节,它只是表现为可以完全访问虚拟的内存区域.

对于Java应用程序,它不是应用程序本身,而是分配了地址空间的Java,而Java默认只是请求一个巨大的地址空间 - 它请求的数量是相对于物理内存大小计算的,但不是因为它有任何需要保守的东西,但仅仅是为了实用性 - 一个应用程序可能不会想要足够的堆大小来完全使服务器瘫痪,所以它的运行假设不会.正如我上面所说,这并不意味着这么多是"分配"的,或者系统不得不花费很多资源这样做.