为了垃圾回收而避免创建对象是否仍然有意义?

Does it still make sense to avoid creating objects for the sake of garbage collection?

例如,在服务适配器中,您可以:

一个。有一个输入数据模型和一个输出数据模型,甚至可能是不可变的,具有不同的 类 并使用对象映射器在 类 之间转换并沿途创建一些短暂的对象

b。有一个单一的数据模型,一些 类 可能是可变的,但为输入创建的相同对象也作为输出发送

还有其他用例,当您必须在包含许多对象的清晰代码和包含较少对象的不太清晰的代码之间做出选择时,我想知道垃圾收集在这个决定中是否仍然有分量。

较新版本的 Java 具有相当复杂的机制来处理非常短暂的对象,因此它不像过去那样糟糕。对于现代 JVM,我会说如果您创建了很多对象,则无需担心垃圾收集时间,这是一件好事,因为现在有更多的对象是动态创建的,这就是这种情况Java.

的旧版本

如果创建的成本很高,例如,将创建的对象数量保持在较低水平,仍然有效。访问数据库以从中检索数据、网络操作等

我应该发表评论,因为 IMO 它不符合答案的条件,但它不适合。

即使答案很可能是——做任何让你的代码更具可读性的事情(老实说,我仍然一直遵循这一点);我们在代码库中遇到了 GC 这个问题。

假设你想创建一个用户图(我们必须 - 大约 1/2 百万)并将他们的所有属性加载到内存中并对它们进行一些聚合和过滤等(这不是我的决定),因为这些图形对象非常重 - 一旦加载 16GB 的堆,JVM 将失败并显示 OOMGC 将需要大量暂停。这是可以理解的 - 大量数据需要大量内存,您不能 运行 离开它。提出并实际起作用的解决方案是使用简单的 BitSets 对其进行建模——其中每一位都是一个 属性 和与其他一些数据的潜在链接;这是迄今为止不可读的,极其维护到今天很复杂。很多变化,很多数据的内在特性——你必须随时知道 3 位的含义,例如,usernameIncome 没有 getter 比方说——你必须做很多事情移动并将其映射到搜索 table 等。但它会使 GC 保持在相当低的水平,至少在我们可以接受的范围内。

因此,除非您能证明 GC 占用了您的应用程序太多时间 - 您可能更安全,只需添加更多 RAM 并增加它(除非您有泄漏)。我仍然会像 99.(99) % 那样选择 clear code

正如其他人所说,我认为最好编写代码以最佳方式解决该问题,而不是考虑垃圾收集器 (GC) 将做什么。

使用 GC 的关键是查看对象的生命周期。堆(通常)分为两个主要区域,称为世代,表示对象存活了多长时间(即年轻代和老年代)。为了尽量减少 GC 的影响,您希望您的对象在它们仍处于年轻代时就符合收集条件(在 Eden space 或幸存者 space 中,但最好是 Eden space ). Eden 中的对象集合 space 实际上是免费的,因为 GC 对它们不做任何事情,它只是忽略它们并在次要 GC 完成时重置分配指针。

与其通过 System.gc() 显式调用 GC,不如调整您的堆。例如,您可以使用 -XX:NewRatio=n 等命令行选项设置新生代的大小,其中 n 表示新旧比例(例如,将其设置为 3 将使比例 new:old 1:3 所以年轻一代将占堆的四分之一)。或者,您可以使用 -XX:NewSize=n-XX:MaxNewSize=m 显式设置大小。 GC 可能会在收集期间调整堆的大小,因此将这些值设置为相同将使其保持固定大小。

您可以分析您的代码以确定对象创建的速率以及您的对象通常存在多长时间。这将为您提供信息(理想情况下)配置您的堆,以最大限度地减少被提升到老年代的对象数量。您真正不希望的是对象被提升然后很快变成垃圾。

或者,您可能想看看 Azul 的 Zing JVM(完全公开,我为他们工作)。这使用了一种不同的 GC 算法,称为 C4,它可以与应用程序线程同时压缩堆,从而消除 GC 对应用程序延迟的大部分影响。