为什么在JVM上固定堆大小?


20

谁能向我解释为什么JVM(我没有检查太多,但是我从未见过这样的情况)为什么需要以固定的堆大小运行?我知道在一个简单的连续堆上实现起来比较容易,但是Sun JVM已有十多年的历史了,因此我希望他们有时间改进它。

需要在启动时定义程序的最大内存大小似乎是在1960年代完成的,然后与OS虚拟内存管理的交互很差(GC检索交换出的数据,无法确定Java进程需要多少内存)真正从OS端使用,浪费了大量VM空间(我知道,您不在乎您喜欢的48位计算机...)。我还猜想,在JVM(EE应用程序服务器,OSGi)内部构建小型操作系统的各种可悲尝试至少部分归咎于这种情况,因为在系统上运行多个Java进程总是会浪费资源,因为您必须给他们每个人可能需要使用的内存。

出乎意料的是,Google并没有像我所期望的那样引起愤怒,但是它们可能只是埋葬在数以百万计的人中,他们发现了固定的堆大小并只是接受了一个事实。


即使没有这种“情况”,EE应用程序服务器的设计也很合理,因为JVM本身需要一些空间,并且在线程之间进行切换比在进程之间进行切换更便宜-这是使Java在90年代后期变得庞大的原因之一。
Michael Borgwardt 2012年

这有点a,我想知道您对此投入了多少思考。例如,如果没有堆限制,GC /交换交互将如何改变?VM空间如何浪费?无论您是否使用它,都将获得2 / 3Gb空间,并且如果您要突破该空间的限制,则无论是固定堆还是浮动堆都无所谓。因此,多个JVM除了交换(如何针对机器的目的进行适当配置)之外,如何浪费其他资源?
kdgregory 2012年

2
这有点麻烦,但是它以多年编写和运行基于Java的平台的日常经验为基础。如果您不进行交换(因为它将使您的系统在20分钟内无响应,直到失控的进程用完分配空间),并且出于稳定性原因关闭了内存过量使用(OOM杀手er不是很好地选择受害者),您关心的是VM空间,与下面的人所暗示的相反,使用-Xmx2048m启动Java VM将立即为具有一个变量的程序分配2GB虚拟内存(至少在我的Linux上是Sun JVM)。
themel 2012年

很好的问题。一直在想同样的事情。但是,q中给出的哪些“事实”和答案是正确的?
马丁·巴

对于您要寻找的反应,只需将Mosey移至sun bug跟踪器...例如此处此处此处。阅读这些内容并感到愤怒:)
基本

Answers:


23

你错了。JVM的堆大小不是固定的,只是有界的:

  • -Xmx设置最大堆内存大小
  • -Xms设置最小堆内存大小

出于几个原因,必须设置上限。首先,它告诉垃圾收集器何时启动。其次,它通过消耗太多内存来防止JVM阻塞整个机器。最小堆大小可能对保留程序至少需要的内存量很有用,以防止程序用完内存(因为其他进程消耗过多的内存)。


9
否最小值是默认值,以避免在需要大量分配导致堆重复增加时启动缓慢的情况,分页将处理物理内存用完
棘手怪胎

@ratchetfreak,这是我的第二次猜测;-)
user281377

@ user281377如果是这种情况,那么C#如何在没有最大堆内存大小的情况下正常运行?
cmorse 2013年

科莫斯:我只能猜测。也许Java的目标是大型服务器,其中许多应用程序共享资源,并且需要严格执行限制,而.net则是为PC和更小,更专用的服务器而创建的。
user281377 2013年

@ user281377:我使用的Java应用程序用尽了堆空间,通常处理起来很差,此后通常只是崩溃或不稳定。ASP.net可以在大型和小型服务器上正常运行。我真正不明白的是,为什么默认情况下Java会强制执行此限制。我很想听听他们决定背后的原因...我相信他们有充分的理由。
cmorse

6

我认为答案与Java的传统有关。它最初被设计为用于嵌入式系统的语言,在嵌入式系统中,显然资源受到限制,您不希望进程简单地吞噬可用的资源。它还可以帮助进行系统管理,因为如果可以设置资源限制,它可以更轻松地在服务器上配置资源。我看到最新的JVM似乎使用了多个非连续堆,尽管当然所有这些在代码中都显示为单个堆。

(FWIW,您必须在Apple的Darwin MacOS之前的版本(无论如何,直到System 7,这是我使用的最后一个)中指定程序的内存要求,而该要求已进入80年代。)


1
+1-是(1)唯一实际解决该问题的答案,并且(2)是合理的。
kdgregory 2012年

2

您需要为GC提供某种机制来告诉它何时运行,否则您的程序将填满整个虚拟内存空间。触发GC的方法有很多:经过的时间,分配的数量,分配的数量,可能还有其他我现在无法想到的方法。IMO这些都不是简单地设置内存边界并在分配的空间达到该边界时运行GC那样好。

关键是要设置正确的边界。我将其-ms视作“这是我的应用程序需要的内存-mx量” ,并视作“永远不要超过此数量”。在生产部署中,如果不相等,则两者应接近,并且应基于实际的,经过测量的需求。

您对“浪费的”虚拟内存的担心放错了地方:它是虚拟的,(几乎)免费。是的,给堆分配过多的内存意味着您不能启动那么多线程,也不能加载那么多内存映射文件。但这是应用程序设计的一部分:您拥有稀缺的资源,需要以允许应用程序运行的方式对它们进行分区。在一个“ C样式”堆中,该堆将一直扩展到您到达内存顶部为止,基本问题是相同的,只是在您遇到麻烦之前不必考虑它。

大堆可能唯一“浪费”的是交换空间,因为所有可写段都需要交换承诺。但这是系统设计的一部分:如果要在同一个盒子上运行许多JVM,请增加交换或减少其堆分配。如果它们开始崩溃,那么您将尝试对系统进行过多操作;购买更多的内存(如果您仍在运行32位处理器,请购买新包装)。


1
但是,运行GC的阈值与固定阈值无关,该阈值不能超出程序的总内存使用量。(实际上,它不应该这样;如果堆很大,并且只有在GC满时才可以进行GC,则GC的周期必定很少但很长)。请参阅分代垃圾回收。

@Ben-是的,您是对的。我的第二句话指出,还有其他选择。但是,我不同意在一般情况下固定大小的堆是管理GC的错误方法。正确调整的JVM使用“正确的”堆大小;以我的经验,当未正确调整JVM时,会出现长时间的GC暂停。通常,“正确的”堆大小远远小于您的想象。
kdgregory 2015年

-2

如user281377所说,您仅指定进程可以消耗多少内存的上限。当然,应用程序本身只会占用其所需的空间。

是否应该存在默认上限是另一个问题,赞成和反对。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.