本着Java的问题:为什么MaxPermSize存在?,我想问一下为什么Sun JVM对其内存分配池的大小使用固定的上限.
默认值是物理RAM的1/4(上限和下限); 因此,如果您有一个需要内存的应用程序,您必须手动更改限制(参数-Xmx),否则您的应用程序将表现不佳,甚至可能会因OutOfMemoryError崩溃.
为什么这个固定限制甚至存在?为什么JVM不会根据需要分配内存,就像本机程序在大多数操作系统上一样?
这将解决Java软件的一大类常见问题(只需谷歌通过设置-Xmx来查看解决问题的网上有多少提示).
编辑:
一些答案指出,这将保护系统的其余部分免受Java程序的影响而导致内存泄漏; 没有限制,这会耗尽整个系统耗尽所有内存.这是事实,但对于任何其他程序同样如此,现代操作系统已经允许您限制程序的最大内存(Linux ulimit,Windows"作业对象").所以这并没有真正回答这个问题,即"为什么JVM与大多数其他程序/运行时环境不同?".
Ste*_*n C 26
为什么这个固定限制甚至存在?为什么JVM不会根据需要分配内存,就像本机程序在大多数操作系统上一样?
原因不是 GC需要事先知道最大堆大小是多少.JVM显然能够将其堆扩展到最大......并且我确信删除该最大值将是一个相对较小的更改.(毕竟,其他Java实现都是这样做的.)同样可以有一种简单的方法来向JVM说"使用尽可能多的内存".
我确信真正的原因是使用所有可用内存保护主机操作系统免受错误Java应用程序的影响.使用无限堆运行可能很危险.
基本上,如果某些应用程序尝试使用所有可用内存,许多操作系统(例如Windows,Linux)会严重降低性能.例如,在Linux上,系统可能会严重颠簸,导致系统上的所有内容都运行得非常慢.在最坏的情况下,系统将无法启动新进程,并且当操作系统拒绝其(合法的)更多内存请求时,现有进程可能会崩溃.通常,唯一的选择是重启.
如果默认情况下JVM使用无界堆运行,那么每当有人运行带有存储泄漏的Java程序时......或者只是试图使用太多内存......它们可能会导致整个操作系统崩溃.
总之,拥有默认的堆绑定是一件好事,因为:
编辑
回应评论:
它并不真正的问题,为什么Sun的JVM的住有界堆,而其他应用程序不中.他们这样做,这样做的好处是(IMO)清楚.也许一个更有趣的问题是为什么其他托管语言默认情况下不会限制其堆.
这些-Xmx和ulimit方法在质量上是不同的.在前一种情况下,JVM完全了解它运行的限制,并有机会相应地管理其内存使用情况.在后一种情况下,典型的C应用程序首先知道的是malloc调用失败的时候.典型的响应是退出错误代码(如果程序检查malloc结果),或者死于分段错误.好的,C应用程序理论上可以跟踪它使用了多少内存,并尝试应对即将发生的内存危机.但这将是艰苦的工作.
Java和C/C++应用程序的另一个不同之处在于,前者往往更复杂,运行时间更长.实际上,这意味着Java应用程序更容易遭受缓慢泄漏.在C/C++案例中,内存管理更难的事实意味着开发人员不会尝试构建具有该复杂性的单个应用程序.相反,他们更有可能通过让一个监听器处理子进程的fork来做东西来构建(比方说)一个复杂的服务......然后退出.这自然减轻了子进程中内存泄漏的影响.
JVM响应"自适应"响应来自操作系统的请求以回忆内存的想法很有意思.但是存在一个很大的问题.为了返回一段内存,JVM首先必须清除段中的任何可到达对象.通常这意味着运行垃圾收集器.但是,如果系统处于内存危机中,那么运行垃圾收集器是你想要做的最后一件事......因为它几乎可以保证产生一连串的虚拟内存分页.
嗯,我会尝试总结到目前为止的答案.
JVM需要对其堆大小进行硬限制,这是没有技术原因的.它可以在没有一个的情况下实现,事实上许多其他动态语言都没有.
因此,为JVM提供堆大小限制只是实现者的设计决策.再猜测为什么这样做有点困难,可能没有一个原因.最可能的原因是它有助于保护系统免受Java程序的内存泄漏,否则可能耗尽所有RAM并导致其他应用程序崩溃或系统崩溃.
Sun可能已经省略了该功能,只是简单地告诉人们使用操作系统原生资源限制机制,但他们可能希望始终有限制,因此他们自己实现了.无论如何,JVM需要知道任何这样的限制(以适应其GC策略),因此使用OS本机机制不会节省太多编程工作.
此外,有一个原因可以解释为什么这样的内置限制对于JVM比没有GC的"普通"程序(例如C/C++程序)更重要:
与具有手动内存管理的程序不同,使用GC的程序实际上并没有明确定义的内存要求,即使使用固定的输入数据也是如此.它只有最低要求,即在给定时间点实际存在(可达)的所有对象的大小总和.但是,在实践中,程序将需要额外的内存来保存死亡但尚未GCed的对象,因为GC无法立即收集每个对象,因为这会导致过多的GC开销.因此,GC只会不时地启动,因此堆上需要一些"喘息空间",死对象可以等待GC.
这意味着使用GC的程序所需的内存实际上是节省内存和良好吞吐量之间的折衷(通过让GC运行较少).因此,在某些情况下,将堆限制设置为低于JVM将尽可能使用的限制可能是有意义的,因此以牺牲性能为代价来节省RAM.要做到这一点,需要有一种方法来设置堆限制.
我认为部分原因与垃圾收集器(GC)的实现有关。GC 通常是惰性的,这意味着只有当堆达到最大大小时,它才会开始真正尝试在内部回收内存。如果您没有设置上限,运行时会很高兴地继续膨胀,直到它用完系统上的所有可用内存为止。
这是因为从应用程序的角度来看,占用更多资源比努力充分利用已有资源的性能更高。这对于 Java 的许多(如果不是大多数)使用来说是有意义的,Java 是一种服务器设置,其中应用程序实际上是服务器上唯一重要的事情。当您尝试用 Java 实现客户端时,它往往不太理想,因为该客户端将同时在数十个其他应用程序中运行。
请记住,对于本机程序,程序员通常会请求资源,但也会显式地清理资源。对于进行自动内存管理的环境来说,情况通常并非如此。
| 归档时间: |
|
| 查看次数: |
9914 次 |
| 最近记录: |