zne*_*eak 317 java garbage-collection
在回答了关于如何用Java强制释放对象的问题(这个人正在清除一个1.5GB的HashMap)后System.gc()
,我被告知System.gc()
手动调用是不好的做法,但这些评论并不完全令人信服.此外,似乎没有人敢投票,也没有人回答我的回答.
我被告知这是不好的做法,但后来我也被告知垃圾收集器运行不再系统地停止世界,并且它也可以有效地被JVM用作提示,所以我有点像茫然
我知道JVM在需要回收内存时通常比你知道的要好.我也明白,担心几千字节的数据是愚蠢的.我也明白,甚至数兆字节的数据也不是几年前的数据.但仍然,1.5千兆字节?而且你知道内存中有1.5 GB的数据; 它不像是在黑暗中拍摄.是System.gc()
系统性的坏,还是有一点它变得好吗?
所以问题实际上是双重的:
System.gc()
?它是否仅仅是某些实现中JVM的提示,还是一个完整的收集周期?真的有垃圾收集器实现可以在不停止世界的情况下完成工作吗?请详细说明人们在评论中对我的回答所做的各种断言.System.gc()
,或者有时候可以接受?如果是这样,那些时候是什么时候?Ste*_*ker 242
每个人总是说要避免的原因System.gc()
是它是一个很好的指标,基本上是破坏了代码.任何依赖于它的正确性的代码肯定会被打破; 任何依赖它来提高性能的东西都很可能被打破.
你不知道你在运行什么样的垃圾收集器.肯定有一些不会像你断言的那样"停止世界",但有些JVM并不是那么聪明或者出于各种原因(也许它们在手机上?)不这样做.你不知道它会做什么.
此外,它不保证做任何事情.JVM可能完全忽略您的请求.
"你不知道它会做什么","你不知道它是否会有所帮助"和"你不应该再打电话"的组合是人们如此强烈地说这一点的原因.你不应该叫它.我认为这是"如果你需要问你是否应该使用它,你不应该"
编辑以解决其他线程中的一些问题:
在阅读了您链接的主题后,还有一些我想指出的内容.首先,有人建议调用gc()
可能会将内存返回给系统.这当然不一定正确 - Java堆本身的增长与Java分配无关.
就像在,JVM将保留内存(数十兆字节)并根据需要增加堆.即使您释放Java对象,它也不一定会将该内存返回给系统; 它可以完全自由地保留分配的内存以用于将来的Java分配.
为了表明它可能System.gc()
什么都不做,请查看:
http://bugs.sun.com/view_bug.do?bug_id=6668279
特别是有一个-XX:DisableExplicitGC VM选项.
Ste*_*n C 145
已经解释过调用system.gc()
可能什么也不做,并且"需要"垃圾收集器运行的任何代码都被破坏了.
然而,调用不好的实际原因System.gc()
是它效率低下.在最糟糕的情况下,它是非常低效的!让我解释.
典型的GC算法通过遍历堆中的所有非垃圾对象来识别垃圾,并推断出任何未访问的对象都必须是垃圾.由此,我们可以模拟垃圾收集的总工作量,其中一部分与实时数据量成比例,另一部分与垃圾量成比例; 即work = (live * W1 + garbage * W2)
.
现在假设您在单线程应用程序中执行以下操作.
System.gc(); System.gc();
Run Code Online (Sandbox Code Playgroud)
第一次调用(我们预测)会(live * W1 + garbage * W2)
工作,并摆脱优秀的垃圾.
第二次通话将(live* W1 + 0 * W2)
起作用并且不会回收.换句话说,我们已经完成了(live * W1)
工作并且完全没有做到.
我们可以将收集器的效率建模为收集垃圾单元所需的工作量; 即efficiency = (live * W1 + garbage * W2) / garbage
.因此,为了使GC尽可能高效,我们需要最大化garbage
运行GC时的值; 即等到堆满了.(而且,使堆尽可能大.但这是一个单独的主题.)
如果应用程序没有干扰(通过调用System.gc()
),GC将在运行之前等待堆已满,从而有效地收集垃圾1.但是如果应用程序强制GC运行,则堆可能不会满,结果将是垃圾收集效率低下.应用程序强制GC的频率越高,GC的效率就越低.
注意:上面的解释掩盖了这样一个事实:典型的现代GC将堆分区为"空格",GC可以动态扩展堆,应用程序的非垃圾对象的工作集可能会有所不同等等.即便如此,同样的基本原则也适用于所有真正的垃圾收集者2.强制GC运行效率很低.
1 - 这就是"吞吐量"收集器的工作原理.CMS和G1等并发收集器使用不同的标准来决定何时启动垃圾收集器.
2 - 我也排除了仅使用引用计数的内存管理器,但是没有当前的Java实现使用这种方法......有充分的理由.
Kit*_*YMG 45
很多人似乎都在告诉你不要这样做.我不同意.如果在加载级别之后的大型加载过程中,您认为:
调用System.gc()没有坏处.我把它看作c/c ++ inline
关键字.这只是gc的一个暗示,你,开发人员,已经决定时间/性能不像通常那么重要,而且其中一些可以用来回收内存.
建议不要依赖它做任何事情是正确的.不要依赖它的工作,但提示现在是一个可接受的时间来收集是完全没问题的.我宁愿浪费时间在代码中无关紧要的地方(加载屏幕),而不是在用户积极与程序交互时(比如游戏级别).
有一次我会强制收集:当试图找出是一个特定的对象泄漏(本机代码或大型,复杂的回调交互.哦和任何UI组件,只要看一眼Matlab.)这永远不应该使用在生产代码中.
小智 31
人们一直在很好地解释为什么不使用,所以我会告诉你几个你应该使用它的情况:
(以下注释适用于使用CMS收集器在Linux上运行的Hotspot,我有信心说System.gc()
实际上总是调用完整的垃圾收集).
在启动应用程序的初始工作之后,您可能是一个糟糕的内存使用状态.您的终身一半的一半可能充满垃圾,这意味着您离第一个CMS更近.在重要的应用程序中,调用System.gc()将堆"重置"为实时数据的起始状态并不是一个坏主意.
与#1一样,如果您密切监视堆使用情况,则需要准确读取基准内存使用情况.如果你的应用程序的正常运行时间的前2分钟都是初始化,那么你的数据将被搞砸,除非你强行(咳咳......"建议")前面的完整gc.
您可能拥有一个应用程序,该程序设计为在运行期间永远不会向终生代推广任何内容.但是,您可能需要预先初始化一些不太大的数据,以便自动转移到终身代.除非您在设置完所有内容后调用System.gc(),否则您的数据可能会在新一代中出现,直到它被提升为止.突然间,您的超级低延迟,低GC应用程序会因为在正常操作期间促进这些对象的巨大(相对来说,当然)延迟而受到攻击.
有时在生产应用程序中使用System.gc调用来验证是否存在内存泄漏.如果您知道X时刻的实时数据集应该与Y时刻的实时数据集保持一定比例,那么调用System.gc()时间X和时间Y并比较内存使用情况会很有用.
GC效率依赖于许多启发式算法.例如,一种常见的启发式方法是对对象的写访问通常发生在不久前创建的对象上.另一个是很多对象都是非常短暂的(有些对象会被使用很长时间,但很多对象会在创建后的几微秒内被丢弃).
通话System.gc()
就像踢GC一样.它意味着:"所有那些经过精心调整的参数,那些聪明的组织,你刚刚分配和管理对象的所有努力,以便事情顺利进行,好吧,只需放弃一切,从头开始".它可以提高性能,但大多数时候它只会降低性能.
要System.gc()
可靠地使用(*),您需要知道GC如何在其所有细节中运行.如果您使用来自其他供应商的JVM,或来自同一供应商的下一个版本,或相同的JVM但具有稍微不同的命令行选项,则此类详细信息会发生相当大的变化.因此,除非您想解决控制所有这些参数的特定问题,否则这很少是个好主意.因此,"不良做法"的概念:这是不被禁止的,方法存在,但它很少得到回报.
(*)我在这里谈论效率.System.gc()
永远不会打破正确的Java程序.它不会产生JVM无法获得的额外内存:在抛出之前OutOfMemoryError
,JVM可以完成工作System.gc()
,即使是最后的手段.
有时(不经常!)你真正了解过去,当前和未来的内存使用情况,然后运行时间.这种情况不会经常发生,我会在提供正常页面时从不在Web应用程序中声明.
许多年前,我在报告生成器上工作
首先,因为它不是实时的并且用户期望等待报告,所以GC运行时的延迟不是问题,但是我们需要以比它们请求更快的速率生成报告.
看一下上面的过程大纲,很明显.
因此,当请求队列为空时,在进行GC运行时显然非常值得; 这没有任何不利因素.
在通过电子邮件发送每个报告后,可能值得进行GC运行,因为我们知道这是GC运行的好时机.但是,如果计算机有足够的RAM,则可以通过延迟GC运行获得更好的结果.
此行为是在每个安装基础上配置的,对于某些客户在每个报告大大加快报告保护后启用强制GC .(我希望这是因为他们的服务器上的内存很少,并且运行了很多其他进程,所以因此很快就迫使GC减少了分页.)
我们从未检测到每次工作队列为空时强制GC运行无效的安装.
这是一个非常麻烦的问题,尽管语言有多么有用,但我觉得很多人反对Java.
事实上,你不能相信"System.gc"做任何事情是令人难以置信的令人生畏的,并且可以很容易地用语言来唤起"恐惧,不确定,怀疑"的感觉.
在许多情况下,处理在重要事件发生之前故意导致的内存峰值是很好的,这会导致用户认为您的程序设计糟糕/没有响应.
能够控制垃圾收集将是一个非常好的教育工具,反过来提高人们对垃圾收集如何工作的理解以及如何使程序利用它的默认行为以及受控行为.
让我来看看这个主题的论点.
通常,程序可能没有做任何事情,而且你知道它没有做任何事情,因为它的设计方式.例如,它可能正在使用大型等待消息框进行某种长时间的等待,并且最后它还可以添加一个收集垃圾的调用,因为运行它的时间将花费很少的时间.漫长的等待,但将避免gc在更重要的操作中起作用.
我不同意,你有什么垃圾收集器并不重要.它的工作是跟踪垃圾并清理垃圾.
通过在使用不太重要的时候调用gc,当你的生活依赖于正在运行的特定代码但是它决定收集垃圾时,你会减少它运行的几率.
当然,它可能不会按照您想要或期望的方式运行,但是当您确实想要调用它时,您就不知道发生了什么,并且用户愿意忍受缓慢/停机.如果System.gc有效,那太好了!如果没有,至少你试过了.除非垃圾收集器具有固有的副作用,如果垃圾收集器在手动调用时假设如何表现,并且这本身会导致不信任.
这是一个无法可靠实现的用例,但如果系统是这样设计的话.这就像制作一个交通信号灯并使其成为一些/所有交通信号灯的按钮不做任何事情,它让你质疑为什么按钮在那里开始,javascript没有垃圾收集功能所以我们不要不要仔细审查它.
什么是"暗示"?什么是"忽略"?计算机不能简单地提示或忽略某些东西,它有一些严格的行为路径,它们可能是动态的,由系统的意图引导.一个正确的答案将包括垃圾收集器在实现级别实际执行的操作,这会导致它在您请求时不执行收集.这个功能只是一个小窍门吗?我必须遇到某种条件吗?这些条件是什么?
就目前而言,Java的GC通常看起来像一个你不相信的怪物.你不知道它什么时候会发生或结束,你不知道它将要做什么,它将如何做.我可以想象一些专家更好地了解他们的垃圾收集如何在每个指令的基础上工作,但绝大多数人只是希望它"只是工作",并且不得不相信一个看似不透明的算法为你工作是令人沮丧的.
阅读某些内容或被教授内容之间存在很大的差距,实际上看到它的实现,跨系统的差异,以及能够在不必查看源代码的情况下使用它.这创造了掌握/理解/控制的信心和感觉.
总而言之,答案存在一个固有的问题"这个功能可能没有做任何事情,我不会详细说明如何判断它什么时候做什么,什么时候不做以及为什么它不会或将会,通常意味着试图去做它只是违背哲学,即使其背后的意图是合理的".
Java GC可能会以它的方式运行,也可能没有,但要了解它,很难真正遵循哪个方向来全面了解GC可以信任的内容以及不要这么做,所以简单地不信任语言就太容易了,因为语言的目的是在哲学范围内控制行为(程序员很容易,尤其是新手从某些系统/语言行为陷入存在危机)你能够容忍(如果你不能,你就不会在必要之前使用这种语言),而且你无法控制的更多事情,因为你无法控制它们,这本身就是有害的.
归档时间: |
|
查看次数: |
115794 次 |
最近记录: |