java 为什么(Sun)JVM 有一个固定的内存使用上限(-Xmx)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3358328/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does the (Sun) JVM have a fixed upper limit for memory usage (-Xmx)?
提问by sleske
In the spirit of question Java: Why does MaxPermSize exist?, I'd like to ask why the Sun JVM uses a fixed upper limit for the size of its memory allocation pool.
本着问题Java:为什么存在 MaxPermSize的精神?,我想问一下为什么Sun JVM对其内存分配池的大小使用固定的上限。
The default is 1/4 of your physical RAM (with upper & lower limit); as a consequence, if you have a memory-hungry application you have to manually change the limit (parameter -Xmx), or your app will perform poorly, possible even crash with an OutOfMemoryError.
默认为物理内存的 1/4(有上限和下限);因此,如果您有一个需要大量内存的应用程序,您必须手动更改限制(参数 -Xmx),否则您的应用程序将表现不佳,甚至可能因 OutOfMemoryError 崩溃。
Why does this fixed limit even exist? Why does the JVM not allocate memory as needed, like native programs do on most operating systems?
为什么这个固定限制甚至存在?为什么 JVM 不像大多数操作系统上的本机程序那样按需分配内存?
This would solve a whole class of common problems with Java software (just google to see how many hints there are on the net on solving problems by setting -Xmx).
这将解决 Java 软件的一大类常见问题(只需谷歌看看网络上有多少关于通过设置 -Xmx 解决问题的提示)。
Edit:
编辑:
Some answers point out that this will protect the rest of the system from a Java program with a run-away memory leak; without the limit this would bring the whole system down by exhausting all memory. This is true, however, it is equally true for any other program, and modern OSes already let you limit the maximum memory for a programm (Linux ulimit, Windows "Job Objects"). So this does not really answer the question, which is "Why does the JVM do it differently from most other programs / runtime environments?".
一些答案指出,这将保护系统的其余部分免受具有失控内存泄漏的 Java 程序的影响;如果没有限制,这将耗尽所有内存,从而导致整个系统崩溃。这是真的,但是,对于任何其他程序也是如此,现代操作系统已经允许您限制程序的最大内存(Linux ulimit,Windows“作业对象”)。所以这并没有真正回答这个问题,即“为什么 JVM 的做法与大多数其他程序/运行时环境不同?”。
采纳答案by sleske
Hm, I'll try summarizing the answers so far.
嗯,我会尝试总结到目前为止的答案。
There is no technical reason why the JVM needs to have a hard limit for its heap size. It could have been implemented without one, and in fact many other dynamic languages do not have this.
JVM 需要对其堆大小设置硬性限制没有技术原因。它可以在没有一个的情况下实现,实际上许多其他动态语言没有这个。
Therefore, giving the JVM a heap size limit was simply a design decision by the implementors. Second-guessing why this was done is a bit difficult, and there may not be a single reason. The most likely reason is that it helps protect a system from a Java program with a memory leak, which might otherwise exhaust all RAM and cause other apps to crash or the system to thrash.
因此,给 JVM 一个堆大小限制只是实现者的设计决定。猜测为什么要这样做有点困难,而且可能没有一个原因。最可能的原因是它有助于保护系统免受内存泄漏的 Java 程序的影响,否则可能会耗尽所有 RAM 并导致其他应用程序崩溃或系统崩溃。
Sun could have omitted the feature and simply told people to use the OS-native resource limiting mechanisms, but they probably wanted to always have a limit, so they implemented it themselves. At any rate, the JVM needs to be aware of any such limit (to adapt its GC strategy), so using an OS-native mechanism would not have saved much programming effort.
Sun 可以省略该功能并简单地告诉人们使用操作系统原生资源限制机制,但他们可能希望始终有一个限制,因此他们自己实现了它。无论如何,JVM 需要注意任何此类限制(以适应其 GC 策略),因此使用 OS 原生机制不会节省太多编程工作。
Also, there is one reason why such a built-in limit is more important for the JVM than for a "normal" program without GC (such as a C/C++ program):
此外,对于 JVM 而言,这种内置限制比没有 GC 的“普通”程序(例如 C/C++ 程序)更重要的原因之一是:
Unlike a program with manual memory management, a program using GC does not really have a well-defined memory requirement, even with fixed input data. It only has a minimum requirement, i.e. the sum of the sizes of all objects that are actually live (reachable) at a given point in time. However, in practice a program will need additional memory to hold dead, but not yet GCed objects, because the GC cannot collect every object right away, as that would cause too much GC overhead. So GC only kicks in from time to time, and therefore some "breathing room" is required on the heap, where dead objects can await the GC.
与手动内存管理的程序不同,使用 GC 的程序实际上并没有明确定义的内存要求,即使输入数据是固定的。它只有一个最低要求,即在给定时间点实际活动(可达)的所有对象的大小总和。然而,在实践中,程序将需要额外的内存来保存已死但尚未被 GC 的对象,因为 GC 无法立即收集每个对象,因为这会导致过多的 GC 开销。因此 GC 只是不时启动,因此堆上需要一些“喘息空间”,死对象可以在那里等待 GC。
This means that the memory required for a program using GC is really a compromise between saving memory and having good througput (by letting the GC run less often). So in some cases it may make sense to set the heap limit lower than what the JVM would use if it could, so save RAM at the expense of performance. To do this, there needs to be a way to set a heap limit.
这意味着使用 GC 的程序所需的内存实际上是节省内存和具有良好吞吐量(通过让 GC 运行较少)之间的折衷。因此,在某些情况下,将堆限制设置为低于 JVM 将使用的限制可能是有意义的,因此以牺牲性能为代价来节省 RAM。为此,需要有一种方法来设置堆限制。
回答by Stephen C
Why does this fixed limit even exist? Why does the JVM not allocate memory as needed, like native programs do on most operating systems?
为什么这个固定限制甚至存在?为什么 JVM 不像大多数操作系统上的本机程序那样按需分配内存?
The reason is NOTthat the GC needs to know before hand what the maximum heap size can be. The JVM is clearly capable of expanding its heap ... up to the maximum ... and I'm sure it would be a relativelysmall change to remove that maximum. (After all, other Java implementations do this.) And it would equally be possible to have a simple way to say "use as much memory as you like" to the JVM.
原因不是GC 需要事先知道最大堆大小可以是多少。JVM 显然能够扩展它的堆......直到最大值......我相信删除该最大值将是一个相对较小的变化。(毕竟,其他 Java 实现也是这样做的。)而且同样可以有一种简单的方法来对 JVM 说“尽可能多地使用内存”。
I'm sure that the real reason is to protect the host operating system against the effects of faulty Java applications using all available memory. Running with an unbounded heap is potentially dangerous.
我确信真正的原因是保护主机操作系统免受使用所有可用内存的错误 Java 应用程序的影响。使用无界堆运行具有潜在危险。
Basically, many operating systems (e.g. Windows, Linux) suffer serious performance degradation if some application tries to use all available memory. On Linux for example, the system may thrash badly, resulting in everything on the system running incredibly slowly. In the worst case, the system won't be able to start new processes, and existing processes may start crashing when the operating system refuses their (legitimate) requests for more memory. Often, the only option is to reboot.
基本上,如果某些应用程序尝试使用所有可用内存,则许多操作系统(例如 Windows、Linux)的性能会严重下降。例如,在 Linux 上,系统可能会严重抖动,导致系统上的所有内容都运行得异常缓慢。在最坏的情况下,系统将无法启动新进程,并且当操作系统拒绝其(合法)请求更多内存时,现有进程可能会开始崩溃。通常,唯一的选择是重新启动。
If the JVM ran with an unbounded heap by default, any time someone ran a Java program with a storage leak ... or that simply tried to use too much memory ... they would risk bringing down the entire operating system.
如果 JVM 在默认情况下以无界堆运行,那么任何时候有人运行一个存在存储泄漏的 Java 程序......或者只是试图使用太多内存......他们都有可能导致整个操作系统崩溃。
In summary, having a default heap bound is a good thing because:
总之,拥有一个默认的堆边界是一件好事,因为:
- it protects the health of your system,
- it encourages developers / users to thinkabout memory usage by "hungry" applications, and
- it potentiallyallows GC optimizations. (As suggested by other answers: it is plausible, but I cannot confirm this.)
- 它可以保护您系统的健康,
- 它鼓励开发人员/用户考虑“饥饿”应用程序的内存使用情况,以及
- 它可能允许 GC 优化。(正如其他答案所建议的:这是有道理的,但我无法证实这一点。)
EDIT
编辑
In response to the comments:
回应评论:
It doesn't really matterwhy Sun's JVMs live within a bounded heap, where other applications don't. They do, and advantages of doing so are (IMO) clear. Perhaps a more interesting question is why other managed languages don'tput a bound on their heaps by default.
The
-Xmxandulimitapproaches are qualitatively different. In the former case, the JVM has full knowledge of the limits it is running under and gets a chance to manage its memory usage accordingly. In the latter case, the first thing a typical C application knows about it is when amalloccall fails. The typical response is to exit with an error code (if the program checks themallocresult), or die with a segmentation fault. OK, a C application could in theory keep track of how much memory it has used, and try to respond to an impending memory crisis. But it would be hard work.The other thing that is different about Java and C/C++ applications is that the former tend to be both more complicated and longer running. In practice, this means that Java applications are more likely to suffer from slow leaks. In the C/C++ case, the fact that memory management is harder means that developers don't attempt to build single applications of that complexity. Rather, they are more likely to build (say) a complex service by having a listener process fork of child processes to do stuff ... and then exit. This naturally mitigates the effect of memory leaks in the child process.
The idea of a JVM responding "adaptively" to requests from the OS to give memory back is interesting. But there is a BIG problem. In order to give a segment of memory back, the JVM first has to clear out any reachable objects in the segment. Typically that means running the garbage collector. But running the garbage collector is the lastthing you want to do if the system is in a memory crisis ... because it is pretty much guaranteed to generate a burst of virtual memory paging.
这其实并不重要,为什么Sun的JVM的住有界堆,而其他应用程序不中。他们这样做,并且这样做的好处是(IMO)清楚的。也许更有趣的问题是为什么其他托管语言默认情况下不对其堆进行限制。
该
-Xmx和ulimit方法是质的不同。在前一种情况下,JVM 完全了解其运行的限制,并有机会相应地管理其内存使用。在后一种情况下,典型的 C 应用程序首先知道的是malloc调用失败的时间。典型的响应是以错误代码退出(如果程序检查malloc结果),或者因分段错误而死亡。好的,C 应用程序理论上可以跟踪它使用了多少内存,并尝试应对即将发生的内存危机。但这会很辛苦。Java 和 C/C++ 应用程序的另一不同之处在于,前者往往更复杂且运行时间更长。在实践中,这意味着 Java 应用程序更容易受到缓慢泄漏的影响。在 C/C++ 情况下,内存管理更难这一事实意味着开发人员不会尝试构建具有这种复杂性的单个应用程序。相反,他们更有可能通过让子进程的侦听器进程分支来执行某些操作……然后退出来构建(例如)复杂的服务。这自然减轻了子进程中内存泄漏的影响。
JVM“自适应”响应来自操作系统的请求以返回内存的想法很有趣。但是有一个大问题。为了返还一段内存,JVM 首先必须清除该段中所有可访问的对象。通常这意味着运行垃圾收集器。但在运行垃圾收集器是最后一个你想要做的,如果系统在内存危机的事情......因为它几乎保证生成的虚拟内存分页一阵。
回答by Mark Peters
I think part of it has to do with the implementation of the Garbage Collector (GC). The GC is typically lazy, meaning it will only start reallytrying to reclaim memory internally when the heap is at its maximum size. If you didn't set an upper limit, the runtime would happily continue to inflate until it used every available bit of memory on your system.
我认为部分原因与垃圾收集器 (GC) 的实现有关。GC 通常是惰性的,这意味着它只会在堆达到最大大小时才开始真正尝试在内部回收内存。如果您没有设置上限,运行时会很高兴地继续膨胀,直到它用完您系统上的所有可用内存。
That's because from the application's perspective, it's more performant to take more resources than exert effort to use the resources you already have to full utilization. This tends to make sense for a lot of (if not most) uses of Java, which is a server setting where the application is literally the only thing that matters on the server. It tends to be slightly less ideal when you're trying to implement a client in Java, which will run amongst dozens of other applications at the same time.
这是因为从应用程序的角度来看,占用更多资源比努力使用您已经拥有的资源来充分利用更高效。这对于 Java 的许多(如果不是大多数)使用来说往往是有意义的,Java 是一种服务器设置,其中应用程序实际上是服务器上唯一重要的事情。当您尝试用 Java 实现客户端时,它往往不太理想,该客户端将同时在数十个其他应用程序中运行。
Remember that with native programs, the programmer typically requests but also explicitly cleans up resources. That isn't typically true with environments who do automatic memory management.
请记住,对于本机程序,程序员通常会请求但也明确地清理资源。对于执行自动内存管理的环境来说,这通常不是真的。
回答by Thorbj?rn Ravn Andersen
It is due to the design of the JVM. Other JVM's (like the one from Microsoft and some IBM ones) can use all the memory available in the system if needed, without an arbitrary limit.
这是由于JVM的设计。如果需要,其他 JVM(例如来自 Microsoft 的 JVM 和一些 IBM 的)可以使用系统中的所有可用内存,而没有任意限制。
I believe it allows for GC-optimizations.
我相信它允许 GC 优化。
回答by Daniel Voina
I think that the upper limit for memory is is linked to the fact that JVM is a VM.
我认为内存的上限与 JVM 是 VM 的事实有关。
As any physical machine has a given (fixed) ammount of RAM so the VM has one.
由于任何物理机都有给定(固定)的 RAM 数量,因此 VM 有一个。
The maximal size makes the JVM easier to manage by the operating system and ensures some performance gains(less swapping).
最大大小使 JVM 更容易被操作系统管理并确保一些性能增益(更少的交换)。
Sun' JVM also works in quite limited hardware architecture(embedded ARM systems) and there the management of resources is crucial.
Sun 的 JVM 也适用于非常有限的硬件架构(嵌入式 ARM 系统),因此资源管理至关重要。
回答by Eddie
One answer that no-one above gave is that the JVM uses both heap and non-heap memory pools. Putting an upper limit on the heap defines not only how much memory is available for the heap memory pools, but it also defines how much memory is available for NON-HEAP usages. I suppose that the JVM could just allocate non-heap at the top of virtual memory and heap at the bottom of virtual memory and grow both toward each other.
上面没有人给出的一个答案是 JVM 同时使用堆内存池和非堆内存池。对堆设置上限不仅定义了有多少内存可用于堆内存池,还定义了有多少内存可用于非堆使用。我想 JVM 可以只在虚拟内存的顶部分配非堆,在虚拟内存的底部分配堆,并相互增长。
Non-heap memory includes the DLLs or SOs that comprise the JVM and any native code being used as well as compiled Java code, thread stacks, native objects, PermGen (meta-data about compiled classes), among other uses. I've seen Java programs crash because so much memory was given to the heap that the application ran out of non-heap memory. This is where I learned that it can be important to reserve memory for non-heap usages by not setting the heap to be too large.
非堆内存包括 DLL 或 SO,这些 DLL 或 SO 包含 JVM 和使用的任何本机代码以及编译的 Java 代码、线程堆栈、本机对象、永久代(关于已编译类的元数据),以及其他用途。我见过 Java 程序崩溃是因为给堆分配了太多内存,以至于应用程序用完了非堆内存。在这里我了解到,通过不要将堆设置得太大来为非堆使用保留内存非常重要。
This makes a much bigger difference in a 32-bit world where an application often has only 2GB of virtual address space than it does in a 64-bit world, of course.
当然,这在 32 位世界中产生了更大的差异,在 32 位世界中,应用程序通常只有 2GB 的虚拟地址空间,而不是在 64 位世界中。
回答by unmaskableinterrupt
Would it not make more sense to separate the upper bound that triggers GC and the maximum that can be allocated ? Once the memory allocated hits the upper-bound, GC can kick in and release some memory to the free pool. sort of like how I clean my desk that I share with my co-worker. I have a large desk, and my threshold of how much junk I can tolerate on the table is much less than the size of my desk. I don't need to have fill up every available inch before I garbage collect.
将触发 GC 的上限和可以分配的最大值分开不是更有意义吗?一旦分配的内存达到上限,GC 就可以启动并向空闲池释放一些内存。有点像我如何清洁我与同事共用的办公桌。我有一张大桌子,我对桌子上能承受多少垃圾的门槛远小于我桌子的大小。在我进行垃圾收集之前,我不需要填满所有可用的英寸。
I could also return some of the desk space that I using to my co-worker, who is sharing my desk....I understand jvms don't return memory back to the system after they've allocated it to themselves, but it does not have to be that way no ?
我还可以将我使用的一些桌面空间归还给与我共享办公桌的同事......我知道 jvm 在将内存分配给自己后不会将内存返回给系统,但它不是必须那样吗?
回答by Matt Crinklaw-Vogt
It does allocate memory as needed, up to -Xmx ;)
它确实根据需要分配内存,最多 -Xmx ;)
One reason I can think of is that once the JVM allocates an amount of memory for its heap, it will never let it go. So if your heap has no upper bound, the JVM may just grab all the free memory on the system and then never let it go.
我能想到的一个原因是,一旦 JVM 为其堆分配了一定数量的内存,它就永远不会放手。因此,如果您的堆没有上限,JVM 可能只会获取系统上的所有空闲内存,然后再也不放手。
The upper bound also tells the JVM when it needs to do a full garbage collection. If your app is still under the upper bound, the JVM will postpone garbage collection and let the memory footprint of your application grow.
上限还告诉 JVM 何时需要进行完整的垃圾收集。如果您的应用程序仍处于上限之下,JVM 将推迟垃圾收集并让您的应用程序的内存占用增加。
Native programs can die due to out of memory errors as well since native applications also have a memory limit: the memory available on the system - the memory already held by other applications.
本机程序也可能因内存不足错误而死,因为本机应用程序也有内存限制:系统上可用的内存 - 其他应用程序已经拥有的内存。
The JVM also needs a contiguous block of system memory in order for garbage collection to be performed efficiently.
JVM 还需要一个连续的系统内存块,以便有效地执行垃圾收集。
EDIT
编辑
Contiguous memory claimor here
The JVM will apparently let some memory go, but it is rarewith the default configuration.
JVM 显然会释放一些内存,但在默认配置下很少见。

