理解 JVM 内存分配和 Java 内存不足:堆空间
Posted
技术标签:
【中文标题】理解 JVM 内存分配和 Java 内存不足:堆空间【英文标题】:Understanding JVM Memory Allocation and Java Out of Memory: Heap Space 【发布时间】:2014-02-11 22:01:21 【问题描述】:我正在研究如何真正了解 JVM 中的内存分配是如何工作的。 我正在编写一个内存不足的应用程序:堆空间异常。
我知道我可以传入 VM 参数(例如 Xms 和 Xmx)来增加 JVM 为正在运行的进程分配的堆空间。这是问题的一种可能解决方案,或者我可以检查我的代码是否存在内存泄漏并在那里解决问题。
我的问题是:
1) JVM 实际上是如何为自己分配内存的?这与操作系统如何将可用内存传递给 JVM 有什么关系?或者更一般地说,任何进程的内存分配实际上是如何工作的?
2) 虚拟内存是如何发挥作用的?假设您有一个具有 32GB 物理内存的系统,并且您将所有 32GB 分配给您的 Java 进程。假设您的进程实际上消耗了所有 32GB 内存,我们如何强制该进程使用虚拟内存而不是遇到 OOM 异常?
谢谢。
【问题讨论】:
如果你还没有使用分析器,比如 VisualVM,那就去做吧。它将让您深入了解应用程序内部发生的情况,例如哪些对象没有被垃圾收集以及何时分配内存。 【参考方案1】:JVM 从操作系统分配 Java 堆内存,然后管理 Java 应用程序的堆。当应用程序创建一个新的 对象,JVM 将堆内存的连续区域子分配给 存储它。堆中被任何其他对象引用的对象 是“活的”,只要它继续存在,它就会一直保留在堆中 参考。不再引用的对象是垃圾,可以 从堆中清除以回收它们占用的空间。 JVM 执行垃圾回收 (GC) 以删除这些对象, 重新组织堆中剩余的对象。 来源:http://pubs.vmware.com/vfabric52/index.jsp?topic=/com.vmware.vfabric.em4j.1.2/em4j/conf-heap-management.html
在使用虚拟内存的系统中,物理内存分为 大小相同的页面。进程寻址的内存也被划分 成相同大小的逻辑页。当一个进程引用一个 内存地址,内存管理器从磁盘获取页面 包括引用的地址,并将其放置在一个空的物理地址中 RAM 中的页面。
来源:http://searchstorage.techtarget.com/definition/virtual-memory
【讨论】:
【参考方案2】:想要分配内存的 JVM(或任何进程)将调用 C 运行时“malloc”函数。此函数维护 C 运行时的堆内存。反过来,它从操作系统内核获取内存——用于此的函数取决于平台;在 Linux 中,它可能使用brk or sbrk 系统调用。
一旦 JVM 获得了内存,它就会自行管理内存,将部分内存分配给正在运行的程序创建的各种对象。
虚拟内存完全由操作系统内核处理。内核管理物理内存页面到各个进程地址空间的映射;如果物理内存少于系统中所有进程所需的物理内存,则操作系统内核会将其中的一部分交换到磁盘。
您不能(也不需要)强制进程使用虚拟内存。它对您的流程是透明的。
如果您遇到“内存不足”错误,那么原因可能是:
已超出 JVM 限制。这些由您在问题中所述的各种命令行参数和/或属性控制
操作系统可能已用完交换空间(或未配置任何交换空间以启动)。或者有些操作系统甚至不支持虚拟内存,在这种情况下你的实际内存已经用完了。
1234563内核会观察。如果进程请求的内存超出限制允许的范围,则尝试将失败(通常这会导致内存不足消息)。【讨论】:
【参考方案3】:JVM 是如何为自己实际分配内存的?
对于堆,它分配一个最大的连续大内存区域。最初这是虚拟内存,但随着时间的推移,它会在操作系统的控制下变成实际内存,用于使用的部分。
这与操作系统如何将可用内存传递给 JVM 有什么关系?
JVM 不知道操作系统中的空闲内存。
或者更一般地说,任何进程的内存分配实际上是如何工作的?
一般它使用malloc和free。
虚拟内存是如何发挥作用的?
最初分配了虚拟内存,这会变成实际使用的内存。这对于任何进程都是正常的。
假设您有一个具有 32GB 物理内存的系统,并且您将所有 32GB 分配给您的 Java 进程。
你不能。操作系统需要一些内存,并且会有用于其他目的的内存。即使在 JVM 中,堆也只是使用的内存的一部分。如果您有 32 GB 的内存,我建议您使用最大 24 GB 堆。
假设你的进程实际上消耗了全部 32GB 内存,
假设您有 48 GB,并且您启动了一个使用 32 GB 主内存的进程。
我们如何强制进程使用虚拟内存而不是遇到 OOM 异常?
应用程序从一开始就使用虚拟内存。你不能让堆太大,因为如果它开始交换你的机器(不仅仅是你的应用程序)将变得不可用。
小心使用堆外内存,您可以使用比物理内存更多的内存。但是托管内存必须在物理内存中,因此如果您需要 32 GB 堆,请购买 64 GB 主内存。
【讨论】:
问题:你能告诉我,为什么 JVM_outOfMemory 是错误而不是异常。 @TusharPandey 来自 Javadoc 8 中的错误An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.
您不希望 catch(Exception e)
捕获此类错误。
在32GB物理内存中,你可以指定JVM使用32GB,因为它一开始是虚拟内存,对吧?即java程序还会启动吗?并且只有在实际尝试使用那么多内存时才会抛出错误?
@ealeon 它会预先保留内存,因此取决于操作系统是否允许这样做。
@PeterLawrey 有人告诉我情况并非如此***.com/questions/54318518/…【参考方案4】:
此博客着眼于 Java 内存利用率,您可能会发现它很有用:
http://www.waratek.com/blog/november-2013/introduction-to-real-world-jvm-memory-utilisation
【讨论】:
很遗憾,此链接不再可用:(以上是关于理解 JVM 内存分配和 Java 内存不足:堆空间的主要内容,如果未能解决你的问题,请参考以下文章