了解 JVM 内存分配和 Java 内存不足:堆空间

我正在研究如何真正理解内存分配在JVM中的工作原理。我正在编写一个应用程序,其中我遇到了内存不足:堆空间异常。

我知道我可以传入VM参数,例如Xms和Xmx,以增加JVM为正在运行的进程分配的堆空间。这是问题的一种可能的解决方案,或者我可以检查代码中的内存泄漏并修复那里的问题。

我的问题是:

1) JVM 实际上如何为自己分配内存?这与操作系统如何将可用内存传送到 JVM 有何关系?或者更一般地说,任何进程的内存分配实际上是如何工作的?

2) 虚拟内存如何发挥作用?假设您有一个具有 32GB 物理内存的系统,并且您将所有 32GB 分配给 Java 进程。假设您的进程实际上消耗了所有32GB的内存,我们如何强制进程使用虚拟内存而不是遇到OOM异常?

谢谢。


答案 1

JVM 实际上如何为自己分配内存?

对于堆,它分配一个最大大小的大连续内存区域。最初,这是虚拟内存,但是,随着时间的推移,它成为操作系统控制下所用部分的真实内存。

这与操作系统如何将可用内存传送到 JVM 有何关系?

JVM 对操作系统中的可用内存一无所知。

或者更一般地说,任何进程的内存分配实际上是如何工作的?

一般来说,它使用malloc和free。

虚拟内存如何发挥作用?

最初分配虚拟内存,这将变成使用的真实内存。这对于任何进程都是正常的。

假设您有一个具有 32GB 物理内存的系统,并且您将所有 32GB 分配给 Java 进程。

你不能。操作系统需要一些内存,并且会有内存用于其他目的。即使在 JVM 中,堆也只是所用内存的一部分。如果您有32 GB的内存,我建议最大为24 GB堆。

假设您的进程实际上消耗了所有32GB的内存,

假设您有48 GB,并且您启动了一个使用32 GB主内存的进程。

我们如何强制该过程使用虚拟内存而不是遇到OOM异常?

应用程序从一开始就使用虚拟内存。您不能使堆太大,因为如果它开始交换,您的计算机(不仅仅是您的应用程序)将变得不可用。

通过谨慎使用堆外内存,可以使用比物理内存更多的内存。但是,托管内存必须位于物理内存中,因此,如果需要 32 GB 的堆,请购买 64 GB 的主内存。


答案 2

想要分配内存的 JVM(或任何进程)将调用 C 运行时 “malloc” 函数。此函数维护 C 运行时的堆内存。反过来,它从操作系统内核获取内存 - 用于此的功能取决于平台;在Linux中,它可能使用brk或sbrk系统调用。

一旦JVM获得了内存,它就会管理内存本身,将其部分分配给由正在运行的程序创建的各种对象。

虚拟内存完全由操作系统内核处理。内核管理物理内存页到各种进程的地址空间的映射;如果物理内存少于系统内所有进程所需的内存,则操作系统内核会将其中一些内存交换到磁盘。

您不能(也不需要)强制进程使用虚拟内存。它对您的流程是透明的。

如果您遇到“内存不足”错误,则原因可能是:

  1. 正在超出 JVM 限制。这些由各种命令行参数和/或属性控制,如您在问题中所述

  2. 操作系统可能已用完交换空间(或未将任何交换空间配置为从开始)。或者某些操作系统甚至不支持虚拟内存,在这种情况下,您的真实内存已经用完。

  3. 大多数操作系统都有管理员限制进程消耗的内存量的工具 - 例如,在Linux中,setrlimit系统调用和/或ulimit shell命令,这两者都设置了内核将遵守的限制。如果进程请求的内存超过限制所允许的内存,则尝试将失败(通常这会导致内存不足消息)。


推荐