为什么(Sun)JVM具有固定的内存使用上限(
本着质疑Java的精神:为什么MaxPermSize存在?,我想问一问,为什么Sun JVM对内存分配池的大小使用固定的上限。
默认值是物理RAM的1/4(带有上限和下限); 因此,如果您有一个需要内存的应用程序,您必须手动更改限制(参数-Xmx),否则您的应用程序将执行得很差,甚至可能会因OutOfMemoryError而崩溃。
为什么这个固定限制甚至存在? 为什么JVM不按需要分配内存,就像本机程序在大多数操作系统上一样?
这将解决Java软件的一整套常见问题(只需通过设置-Xmx来查看网络上有多少提示来解决问题)。
编辑:
有些答案指出,这将保护系统的其他部分免受Java程序的内存泄漏; 没有限制,这会耗尽所有内存,从而降低整个系统的性能。 但是,对于其他任何程序都是如此,现代操作系统已经允许您限制程序的最大内存(Linux ulimit,Windows“Job Objects”)。 所以这并没有真正回答这个问题,即“为什么JVM与大多数其他程序/运行时环境不同?”。
为什么这个固定限制甚至存在? 为什么JVM不按需要分配内存,就像本机程序在大多数操作系统上一样?
原因并不是 GC需要事先知道最大堆大小是多少。 JVM显然能够扩展它的堆......最大限度地......我相信这将是一个相对较小的变化来消除这个最大值。 (毕竟,其他的Java实现都是这样做的。)同样可以有一种简单的方法来向JVM说“尽可能多地使用内存”。
我确信真正的原因是使用所有可用内存来保护主机操作系统免受故障Java应用程序的影响。 以无限堆运行有潜在危险。
基本上,如果某些应用程序尝试使用所有可用内存,许多操作系统(例如Windows,Linux)都会出现严重的性能下降。 例如,在Linux上,系统可能会出现严重的瘫痪,导致系统上的所有内容都运行得非常慢。 在最坏的情况下,系统将无法启动新进程,并且当操作系统拒绝他们的(合法)更多内存请求时,现有进程可能开始崩溃。 通常,唯一的选择是重新启动。
如果默认情况下JVM运行的是无限堆,任何时候有人运行带有存储泄漏的Java程序......或者只是试图使用太多的内存......他们将冒风险降低整个操作系统的风险。
总之,有一个默认的堆绑定是一件好事,因为:
编辑
在回应评论时:
Sun的JVM为什么生活在一个有限的堆中,其他应用程序没有。 他们这样做,并且这样做的好处是(海事组织)明确。 也许更有趣的问题是为什么其他托管语言默认情况下不会在堆上绑定。
-Xmx
和ulimit
方法在性质上是不同的。 在前一种情况下,JVM完全了解它正在运行的限制,并有机会相应地管理其内存使用情况。 在后一种情况下,典型的C应用程序知道的第一件事是malloc
调用失败。 典型的响应是用一个错误代码退出(如果程序检查malloc
结果),或者死于分段错误。 好的,C应用程序理论上可以跟踪它使用了多少内存,并尝试回应即将发生的内存危机。 但这将是艰苦的工作。
Java和C / C ++应用程序的另一个不同之处在于,前者往往更复杂且运行时间更长。 实际上,这意味着Java应用程序更容易受到缓慢泄漏的影响。 在C / C ++的情况下,内存管理更难的事实意味着开发人员不会尝试构建具有这种复杂性的单个应用程序。 相反,他们更可能通过让侦听器处理子进程的fork来完成(说)一个复杂的服务,然后退出。 这自然地减轻了子进程中内存泄漏的影响。
JVM响应“自适应”来自操作系统的要求提供内存的请求很有意思。 但是有一个大问题。 为了给回一段内存,JVM首先必须清除段中的任何可到达的对象。 通常这意味着运行垃圾收集器。 但是,如果系统处于内存危机中,运行垃圾收集器是您想要做的最后一件事情......因为它几乎可以保证产生一阵虚拟内存分页。
嗯,我会尝试总结迄今为止的答案。
没有技术上的原因,为什么JVM需要对其堆大小进行硬性限制。 它可能没有一个实现,事实上许多其他动态语言都没有这个。
因此,给JVM一个堆大小限制仅仅是实现者的设计决定。 猜测为什么这样做是有点困难,并且可能没有单一的原因。 最可能的原因是它有助于保护系统免受Java程序的内存泄漏,否则可能会耗尽所有内存并导致其他应用程序崩溃或系统崩溃。
Sun可能已经省略了该功能,并简单地告诉人们使用操作系统原生资源限制机制,但他们可能总希望有一个限制,所以他们自己实现了它。 无论如何,JVM需要知道任何这样的限制(以适应其GC策略),因此使用OS本机机制不会节省大量编程工作。
另外,为什么这种内置限制对于JVM而言比没有GC的“正常”程序(例如C / C ++程序)更重要的原因之一是:
与手动内存管理的程序不同,即使使用固定输入数据,使用GC的程序也没有真正的内存需求。 它只有一个最低要求,即在给定时间点实际存在(可达)的所有对象的大小总和。 但是,实际上,程序需要额外的内存来保存已死的但尚未GCed的对象,因为GC不能立即收集每个对象,因为这会导致太多的GC开销。 所以GC只是不时地踢,因此堆上需要一些“呼吸室”,在那里死物可以等待GC。
这意味着使用GC的程序所需的内存实际上是保存内存和获得良好吞吐量(通过减少GC运行频率)之间的折衷方案。 因此,在某些情况下,将堆的限制设置为低于JVM使用的堆的限制可能是有意义的,因此可以节省内存而牺牲性能。 要做到这一点,需要有一种方法来设置堆的限制。
我认为它的一部分与垃圾收集器(GC)的实施有关。 GC通常是懒惰的,这意味着只有当堆处于最大尺寸时,它才会真正开始尝试在内部回收内存。 如果您没有设置上限,运行时就会继续膨胀,直到它使用系统上的所有可用内存位。
这是因为从应用程序的角度来看,获取更多资源比努力使用已有的资源来充分利用资源要更高效。 这对Java的很多(如果不是最多的)用法都是有意义的,这是一个服务器设置,应用程序实际上是唯一对服务器重要的事情。 当您尝试使用Java实现一个客户端时,它会变得稍微不理想,而客户端可能会同时在几十个其他应用程序中运行。
请记住,对于本机程序,程序员通常会请求但明确清理资源。 这对于执行自动内存管理的环境通常不适用。
链接地址: http://www.djcxy.com/p/82923.html上一篇: Why does the (Sun) JVM have a fixed upper limit for memory usage (