【问题标题】:32 bit JVM commits >3G virtual memory32 位 JVM 提交 >3G 虚拟内存
【发布时间】:2011-12-18 15:45:57
【问题描述】:

我们有一个在 64 位 RHEL5 下运行的 32 位 JVM,它有足够的内存 (32G)。由于不同的原因,这个过程需要一个相当大的托管堆和永久空间——目前,它使用以下 VM 参数运行:

-Xmx2200M -XX:MaxPermSize=128M -XX:+CMSClassUnloadingEnabled

我最近开始看到 JVM 崩溃,因为它 - 似乎 - 耗尽了本机内存(它无法创建本机线程,或者无法分配本机内存等)。这些崩溃与托管堆的状态没有(直接)相关,因为当这些崩溃发生时,托管堆大约 50-70% 已满。 我知道为托管进程保留的内存接近 2.5 G,这给 JVM 本身留下的内存不超过 0.5G,但是 - 我不明白为什么 0.5 对 JVM 来说还不够,即使有持续的 GCing 正在进行 - 真正的问题是:当我使用 jconsole 连接到进程时,它会说(当前)

Committed virtual memory: 
3,211,180 kbytes

这比 3G 还多。我可以想象出于某种原因 JVM 认为它有 3,211,180 kbytes (3.06G) 内存,但当它尝试超过 3G 时,内存分配失败。

任何想法 a) 为什么会这样 b)如何避免这种情况

谢谢。 伴侣

【问题讨论】:

  • 在 64 位下,Windows 32 位进程使用完整的 4GB 地址空间 - 操作系统不再需要在 32 位地址空间中保留空间,因为它可以使用 64 位地址空间。
  • 好的,刚刚注意到这是 linux 的标签,我仍然认为它以类似的方式工作
  • 好吧,如果是,那我还是不明白为什么它会耗尽本机内存。 ://
  • 如果“本机内存”是指“物理内存”,那么它可能不是 - 如果您有交换分区,那么操作系统会根据需要将物理内存分页到磁盘上。虚拟地址空间更可能是碎片化的,即即使虚拟地址空间中有空闲空间,空闲空间也被分成许多小块,其中最大的块太小而无法满足所需的分配。
  • 是的,很可能是地址空间不足。即使没有碎片,当你有 3.5G 的 JVM 堆时,0.5G 的机器堆也不是很多。对于大量的小对象,GC 本身就可能需要这么多,更不用说线程、类等的非 GC 部分了。

标签: java linux memory-management jvm 32bit-64bit


【解决方案1】:

在一个典型的虚拟机中有很多开销没有计入虚拟机核算,因为它本质上是被进程的本机元素窃取的——例如。用于执行系统库的本机级别代码的 .so 文件中的映射不计入基本 VM 记帐。您的典型共享库映射到内存的顶部 GB,因此,如果您尝试将内存分配到该区域,您将被拒绝,因为它会溢出共享库的内存区域 - 大多数操作系统上的内存分配是由一个当您要求更多内存时会出现简单的条。当您请求内存并且 bar 与其他用途发生冲突时,它就会失败。下面的大部分细节都是关于这个的。

您需要避免在 32 位进程中需要这么多内存。这是根本的挑战。获得一个 64 位 VM 是微不足道的,它允许您使用比其他方式可访问的更多的内存 - 它只是在这种情况下可用。

如果您使用的是 32 位进程,则很有可能遇到 32 位进程的有效地址空间限制。对于 Windows,最大约为 3GB - 任何高于此的空间都保留给 I/O 空间和内核。您可以移动它,但它可能会破坏为 32 位操作系统设计的应用程序/驱动程序。

对于 Linux,每个进程最终会获得约 3GB 的可用可寻址 RAM,其余部分被内核等用完并映射到共享库中。该限制称为“地址空间限制”,我认为可以调整。

如何避免?嗯,在大多数情况下,你不能,这是 32 位地址空间的物理限制,并且需要将内核/IO 与 32 位操作系统的进程放在相同的地址空间中。

使用 64 位操作系统,您可以使用(大部分)所有 64 位地址空间,这远远超过您需要使用的空间。

【讨论】:

  • 嗨,谢谢——我想我了解内存限制和地址空间限制的含义。我的主要问题是我不明白为什么 JVM 会耗尽本机内存——我认为 0.5G 应该足够用于 GC 等临时空间。我们当然可以迁移到 64 位 JVM,但最好知道是什么原因导致 JVM 使用 700M 额外空间,但是没有很好的工具来调试 JVM 本身。
  • 从 jmap 开始 - 它相当于 java 进程的 pmap。可能性是它没有用完“实际内存”,而是用完了可用内存。您应该查看从 /proc/<pid>/maps 生成的内存映射 - 它说明了应用程序的内存利用率。内存使用不仅仅是您声明的基数,您还拥有 JIT 内存、保护页,映射到共享对象中。如果您在一个进程中有 350MB 的内存变化,那么您没有足够的内存池。这是长期服务中的常见问题。
  • 嗨,谢谢——我一般不相信池化托管对象,因为基本上你在 GC 顶部放置了一个池化层,它本身具有池化逻辑。关于 jmap:我看到了映射共享对象的内存占用,但这似乎可以忽略不计。由于某种原因,JVM 内部似乎消耗了太多(?)内存。
【解决方案2】:

当您启动 JVM 时,它会立即为其分配最大大小。使用多少内存并不重要。您的应用程序可以处理大约 3 GB,其中大约 2.3 GB 您已分配给 heap 和 perm gen。其余部分可用于共享库(通常约为 200 MB)和线程堆栈。

当解决方案相对简单(使用 64 位 JVM)时,担心为什么不能使用完整的 3 GB 地址并不是很有用 我假设您没有任何共享库可用于 32 位。但是,如果您确实有其他共享库,则它们很容易使用 100 MB。

【讨论】:

    猜你喜欢
    • 2014-03-12
    • 2011-03-26
    • 1970-01-01
    • 2011-03-19
    • 2011-07-23
    • 1970-01-01
    • 2011-06-26
    • 2012-02-27
    • 2021-11-17
    相关资源
    最近更新 更多