当前位置: 首页 > 面试题库 >

积极的垃圾收集器策略

锺离飞尘
2023-03-14
问题内容

我正在运行一个创建并忘记大量对象的应用程序,长期存在的对象的数量确实增长缓慢,但是与寿命短的对象相比,这很少。这是具有高可用性要求的桌面应用程序,需要每天24小时开机。大多数工作是在单个线程上完成的,该线程将使用它可以使用的所有CPU。

过去,我们在繁重的负载下看到了以下内容:随着垃圾收集器收集的内存少于新分配的内存,使用的堆空间缓慢增加,使用的堆大小缓慢增长并最终接近指定的最大堆。届时,垃圾收集器将投入大量资源并开始使用大量资源,以防止超出最大堆大小。这会减慢应用程序的速度(慢10倍),并且在大多数情况下,GC会在几分钟后成功清理垃圾或失败并抛出垃圾OutOfMemoryException,这两个选项实际上都不可接受。

使用的硬件是四核处理器,至少有4GB的内存运行64位Linux,我们可以根据需要使用所有这些处理器。当前,该应用程序大量使用单个内核,而该内核大部分时间都在运行单个内核/线程。其他核心大部分处于空闲状态,可以用于垃圾回收。

我觉得垃圾收集器应该在内存耗尽之前尽早更积极地进行收集。我们的应用程序没有任何吞吐量问题,低暂停时间要求比吞吐量更重要,但远没有达到最大堆大小重要。如果单个繁忙线程仅以当前速度的75%运行,这是可以接受的,只要这意味着垃圾收集器可以跟上创建速度。简而言之,性能的稳步下降要好于我们现在看到的突然下降。

我已经仔细阅读了Java SE 6 HotSpot
[tm]虚拟机垃圾收集调优
,这意味着我很了解这些选项,但是由于我的要求与本文所讨论的内容有些不同,因此我仍然很难选择正确的设置。 。

目前,我正在使用带有option的ParallelGC
-XX:GCTimeRatio=4。这比时间比率的默认设置要好一些,但是我觉得该设置允许GC运行的次数更多。

为了监视,我主要使用jconsole和jvisualvm。

我想知道您针对上述情况建议使用哪些垃圾收集选项。另外,我可以查看哪个GC调试输出以更好地理解瓶颈。

编辑:
我知道这里的一个很好的选择是创建更少的垃圾,这是我们真正考虑的问题,但是我想知道我们如何通过GC调整解决此问题,因为这是我们可以更轻松地做并滚动的事情比更改大量源代码要快得多。另外,我运行了不同的内存探查器,我了解了垃圾的用途,并且据我所知,垃圾由可以收集的对象组成。

我在用:

html" target="_blank">java version "1.6.0_27-ea"
Java(TM) SE Runtime Environment (build 1.6.0_27-ea-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.2-b03, mixed mode)

使用JVM参数:

-Xmx1024M and -XX:GCTimeRatio=4

编辑以回复Matts的评论:
大多数内存(和cpu)用于构造代表当前情况的对象。随着情况的迅速变化,其中一些将立即被丢弃,如果一段时间内没有任何更新,则另一些将处于中等寿命。


问题答案:

您无需提及正在运行的JVM的哪个内部版本,这是至关重要的信息。您也没有提到应用程序倾向于运行多长时间(例如,工作时间长短还是一周还是更少?)

其他几点

  1. 如果您由于分配速度比年轻一代的速度快而不断泄漏对象的使用权,那么您的世代大小将不正确。您将需要对应用程序的行为进行一些适当的分析,以便能够正确调整它们的大小,您可以为此使用visualgc。
  2. 吞吐量收集器旨在接受单个较大的停顿,而不是许多较小的停顿,其好处是它是紧凑的收集器,可实现更高的总吞吐量
  3. CMS的存在是为了服务于频谱的另一端,即有更多得多的更小的停顿,但总吞吐量却更低。不利的一面是它没有压缩,所以碎片可能是一个问题。碎片问题在6u26中得到了改善,因此,如果您不在该版本上,则可能是升级时间。请注意,您提到的“流血到长期使用”效应加剧了碎片问题,并且在一定的时间内,这将导致升级失败(也就是计划外的完整gc和关联的STW暂停)。我以前就这个问题写过一个答案
    1. 如果运行的RAM> 4GB的64位JVM和最近使用的JVM足够多,请确保您-XX:+UseCompressedOops只是在浪费空间,因为对于没有它的相同工作负载,64位JVM的空间约为32位JVM的1.5倍(并且如果不是,请升级以访问更多RAM)

您可能还需要阅读我针对该主题写的另一个答案,该答案可以用来确定幸存者的空间并适当调整伊甸园的大小。基本上,您想要实现的是;

  • 伊甸园足够大,不会经常收集
  • 幸存者空间的大小与保有权期限相匹配
  • 设置使用期限阈值,以尽可能确保只有真正长期存在的对象才能使用该期限

因此,假设您有一个6G堆,您可能会做类似5G eden + 16M幸存者空间+任期阈值1的操作。

基本过程是

  1. 分配到伊甸园
  2. 伊甸园充满了
  3. 活物扫向幸存者空间
  4. 幸存者空间中的活对象要么复制到到空间,要么提升为保有期限(取决于保有权阈值和可用空间,并且没有将它们从1复制到另一个的次数)
  5. 伊甸园里剩下的一切都被清除了

因此,如果给定的空间大小适合您的应用程序的分配配置文件,则完全有可能对系统进行配置,使其能够很好地处理负载。一些注意事项;

  1. 您需要一些长时间运行的测试才能正确执行此操作(例如,可能需要几天时间才能解决CMS碎片问题)
  2. 您需要多次进行每次测试才能获得良好的结果
  3. 您需要在GC配置中一次更改1件事
  4. 您需要能够向应用程序呈现合理的可重复工作量,否则很难客观地比较不同测试运行的结果
  5. 如果工作负载不可预测并且具有巨大的高峰/低谷,那么这将很难可靠地完成。

1-3点表示这可能需要一段时间才能正确。另一方面,您可能能够很快使它变得足够好,这取决于您的肛门状况!

最后,与彼得·劳里(Peter Lawrey)的观点相呼应,如果您对对象分配确实非常严格,则可以省去很多麻烦(尽管引入了其他麻烦)。



 类似资料:
  • Java 15 使 ZGC、Z 垃圾收集器成为标准功能。它是 Java 15 之前的一个实验性功能。它是低延迟、高度可扩展的垃圾收集器。 ZGC 是在 Java 11 中作为一项实验性功能引入的,因为开发人员社区认为它太大而无法提前发布。 即使在机器学习应用程序等海量数据应用程序的情况下,ZGC 也具有高性能和高效工作。它确保在处理数据时不会因垃圾收集而长时间停顿。它支持 Linux、Window

  • Java 15 使 ZGC、Z 垃圾收集器成为标准功能。它是 Java 15 之前的一个实验性功能。它是低延迟、高度可扩展的垃圾收集器。 ZGC 是在 Java 11 中作为一项实验性功能引入的,因为开发人员社区认为它太大而无法提前发布。从那时起,对这个垃圾收集做了很多改进,例如 - 并发类卸载 取消提交未使用的内存 支持班级数据共享 NUMA 多线程堆Pre-touch 最大堆大小限制从 4 T

  • 问题内容: 我从带有node.js的线程垃圾收集中学到了node.js使用世代GC。 我通常使用循环对象引用(最终我都会删除/确保超出范围),并想知道node.js是否能很好地处理它们。所以例如。如果使用参考完成。计数,会有一个问题,所以我想知道这个节点有多好。 一些使用场景: 对于每个http请求,我创建一个带有lambda的setTimeout,该lambda可能引用了范围对象。作用域对象还引

  • Kubernetes 垃圾收集器的角色是删除指定的对象,这些对象曾经有但以后不再拥有 Owner 了。 注意:垃圾收集是 beta 特性,在 Kubernetes 1.4 及以上版本默认启用。 Owner 和 Dependent 一些 Kubernetes 对象是其它一些的 Owner。例如,一个 ReplicaSet 是一组 Pod 的 Owner。具有 Owner 的对象被称为是 Owner

  • 今天我参加了面试我是一个新手到java,Spring启动。面试官问了关于垃圾收集器的问题。我说垃圾收集器将释放未使用的资源。然后他问了关于IOC容器,我说它采取了对象创建的控制权,并将注入到依赖bean。然后他问为什么我们需要使用一个IOC容器我说它不会创建一个新对象每次它将使用一个现有的。再次他问好吧创建一个新对象的问题是什么,因为垃圾收集器将释放内存那么为什么我们应该去IOC...?我被困在这

  • 来自MSDN:“当一个对象不可访问时,垃圾收集器会将该对象视为垃圾。然后,当垃圾收集器将一个对象的条目从终结队列移动到freachable队列时,该对象不再被视为垃圾,其内存也不会被回收。此时,垃圾收集器已完成对垃圾的识别。一些被识别为垃圾的对象已被重新分类。”被归类为非垃圾。垃圾收集器压缩可回收内存,特殊运行时线程清空可回收队列,执行每个对象的Finalize方法。需要两个GC来回收需要终结的对