ChatGPT解决这个技术问题 Extra ChatGPT

System.gc() 什么时候做某事?

我知道垃圾收集在 Java 中是自动化的。但我知道,如果您在代码中调用 System.gc(),JVM 可能会或可能不会决定在此时执行垃圾收集。这如何精确地工作?当看到 System.gc() 时,JVM 究竟根据什么基础/参数决定执行(或不执行)GC?

是否有任何示例在这种情况下将其放入您的代码中是个好主意?

一个概念过于复杂,无法在此处详细介绍,但本文应该可以帮助您:chaoticjava.com/posts/how-does-garbage-collection-work
确切的行为取决于 JVM,即依赖于实现。

j
jodonnell

在实践中,它通常决定进行垃圾回收。答案因许多因素而异,例如您正在运行的 JVM、它所处的模式以及它使用的垃圾收集算法。

我不会在您的代码中依赖它。如果 JVM 即将抛出 OutOfMemoryError,调用 System.gc() 不会阻止它,因为垃圾收集器会在它达到那个极端之前尝试尽可能多地释放它。我唯一一次看到它在实践中使用是在 IDE 中,它附加到用户可以单击的按钮上,但即使在那里它也不是非常有用。


您可以在 jconsole 中强制进行垃圾收集
@bene你真的能“强迫”它吗? jconsole 只是调用 System.gc() 吗?
我会在视频游戏的加载屏幕中使用 System.gc()。那时,我真的不在乎电话是否清除了它可能清除的所有内容,或者什么也没做。然而,我更喜欢它在加载屏幕期间而不是在游戏过程中“花费精力回收未使用的对象”。
G
Guillermo Vasconcelos

我能想到调用 System.gc() 的唯一示例是在分析应用程序以搜索可能的内存泄漏时。我相信分析器会在拍摄内存快照之前调用此方法。


是的,我也是这样做的。例如,由 Runtime.freeMemory() 返回的值只有在 System.gc() 之后才真正有意义,因为通常您想知道无法收集的实例阻塞了多少内存。当然,仅用于调试/内存分析,从不用于生产。
不,它也适用于许多其他场景。例如,假设您有一个可感知生命周期的模式。在 onStop() 中,如果您是 null(ing) 实例,最好调用 System.gc() 来帮助它。
N
Narbhakshi

您无法控制 Java 中的 GC——VM 决定。我从未遇到过需要的情况。由于 System.gc() 调用只是简单地表明 VM 进行垃圾回收,并且它还进行了完整的垃圾回收(多代堆中的旧代和新代),因此它实际上会导致 MORE cpu需要消耗的周期。

在某些情况下,向 VM 建议它现在进行完整收集可能是有意义的,因为您可能知道应用程序将在接下来的几分钟内处于空闲状态,然后才会发生繁重的工作。例如,在应用程序启动期间初始化大量临时对象之后(即,我刚刚缓存了大量信息,我知道一分钟左右我不会获得太多活动)。想想像eclipse这样的IDE启动——它做了很多初始化,所以也许在初始化之后立即进行一次完整的gc是有意义的。


P
Pierre Laporte

Java 语言规范不保证 JVM 会在您调用 System.gc() 时启动 GC。这就是“可能会或可能不会决定在那个时候进行 GC”的原因。

现在,如果您查看 OpenJDK source code,它是 Oracle JVM 的主干,您会看到对 System.gc() 的调用确实会启动一个 GC 周期。如果您使用其他 JVM,例如 J9,您必须查看他们的文档以找出答案。例如,Azul 的 JVM 有一个持续运行的垃圾收集器,因此调用 System.gc() 不会做任何事情

其他一些答案提到在 JConsole 或 VisualVM 中启动 GC。基本上,这些工具会远程调用 System.gc()

通常,您不想从代码中启动垃圾回收周期,因为它会混淆应用程序的语义。您的应用程序执行一些业务操作,JVM 负责内存管理。你应该把这些问题分开(不要让你的应用程序做一些内存管理,专注于业务)。

但是,在少数情况下调用 System.gc() 可能是可以理解的。例如,考虑微基准。没有人希望在微基准测试中发生 GC 循环。因此,您可以在每次测量之间触发 GC 循环,以确保每次测量都以空堆开始。


P
Pimgd

如果您调用 System.gc(),您需要非常小心。调用它会给您的应用程序添加不必要的性能问题,并且不能保证实际执行收集。实际上可以通过 java 参数 -XX:+DisableExplicitGC 禁用显式 System.gc()

我强烈建议您通读 Java HotSpot Garbage Collection 中提供的文档,以了解有关垃圾收集的更多详细信息。


我的 android 应用程序总是向我抛出 OutOfMemoryException。所以我想在我的类的 onDestroy 函数中使用 System.gc() 来清除所有不需要的引用和对象。这是一个好方法吗
@Sagar Devanga 按照您的描述手动调用 gc 不太可能有帮助。在抛出 OOM 之前,JVM 已经尝试进行垃圾收集以释放内存
N
Narbhakshi

System.gc() 由 VM 实现,它的作用是特定于实现的。例如,实施者可以简单地返回并且什么都不做。

至于何时发出手动收集,您可能想要这样做的唯一时间是当您放弃包含大量较小收集的大型收集(例如 Map<String,<LinkedList>>)并且您想尝试获取性能那时和那里,但在大多数情况下,你不应该担心它。 GC 比你更了解——可悲的是——大多数时候。


e
eckes

如果您使用直接内存缓冲区,即使您的直接内存不足,JVM 也不会为您运行 GC。

如果您调用 ByteBuffer.allocateDirect() 并收到 OutOfMemoryError ,您会发现手动触发 GC 后此调用很好。


您是说 System.gc() 收集直接分配,而 JVM 通常不会(在抛出 OOM 之前)。因为我认为这是错误的。在显式 GC 之后分配可能会成功的唯一原因是额外的时间和更多可能释放终结器的堆内对象(并释放一些直接分配的对象)。
在最新版本的 Java 6 中,allocateBuffer 中的代码将始终在抛出 OutOfMemoryError 之前运行 System.gc()。在最终化代码中,它有一个直接内存使用的 Cleaners 的快捷方式。即它不会被终结器线程释放,因为这可能太慢了。即便如此,如果您特别快地创建直接内存区域,也有可能获得 OOME。解决方案是不这样做,并尽可能重用您的直接内存区域。
谢谢,这听起来更像我的经历。所以我猜这个答案只对较旧的 Java 版本是正确的。
C
Carl Manaster

大多数 JVM 会启动 GC(取决于 -XX:DiableExplicitGC 和 -XX:+ExplicitGCInvokesConcurrent 开关)。但是该规范的定义不太好,以便以后更好地实现。

规范需要澄清:Bug #6668279: (spec) System.gc() should indicate that we don't recommend use and don't guarantee behaviour

RMI 和 NIO 在内部使用 gc 方法,它们需要同步执行,目前正在讨论中:

Bug #5025281: Allow System.gc() to trigger concurrent (not stop-the-world) full collections


T
Tamas Rev

Garbage CollectionJava 中很好,如果我们在 Desktop/laptop/server 中执行用 java 编码的软件。您可以在 Java 中调用 System.gc()Runtime.getRuntime().gc()

请注意,这些调用都不能保证做任何事情。它们只是 jvm 运行垃圾收集器的建议。是否运行 GC 取决于 JVM。所以,简短的回答:我们不知道它什么时候运行。更长的答案:如果有时间,JVM 会运行 gc。

我相信,这同样适用于 Android。但是,这可能会降低您的系统速度。


如果有时间,JVM 会运行 gc:并非总是如此,当 Eden 内存已满时,JVM 可以运行 gc。
佚名

通常,VM 会在抛出 OutOfMemoryException 之前自动进行垃圾收集,因此添加显式调用应该无济于事,除非它可能会将性能影响移到更早的时刻。

但是,我想我遇到了一个可能相关的案例。不过我不确定,因为我还没有测试它是否有任何效果:

当您对文件进行内存映射时,我相信当足够大的内存块不可用时,map() 调用会引发 IOException。我认为,map() 文件之前的垃圾收集可能有助于防止这种情况发生。你怎么看?


J
Jackie

我们永远不能强制垃圾收集。 System.gc 只是建议使用 vm 进行垃圾收集,但是,该机制到底什么时候运行,没人知道,这是 JSR 规范中规定的。


K
Kyune

花时间测试各种垃圾收集设置有很多话要说,但正如上面提到的,这样做通常没有用处。

我目前正在从事一个涉及内存有限环境和相对大量数据的项目——有一些大数据将我的环境推到了极限,即使我能够降低内存使用量理论上它应该可以正常工作,但我仍然会收到堆空间错误——详细的 GC 选项向我显示它正在尝试垃圾收集,但无济于事。在调试器中,我可以执行 System.gc() 并且果然会有“大量”可用内存......不是很多额外的,但足够了。

因此,我的应用程序调用 System.gc() 的唯一时间是当它即将进入将分配处理数据所需的大缓冲区的代码段时,并且对可用空闲内存的测试表明我不是保证拥有它。特别是,我正在查看一个 1gb 环境,其中至少 300mb 被静态数据占用,大部分非静态数据与执行相关,除非正在处理的数据恰好至少为 100-200 MB来源。这都是自动数据转换过程的一部分,因此从长远来看,数据都存在相对较短的时间。

不幸的是,虽然有关调整垃圾收集器的各种选项的信息是可用的,但它似乎主要是一个实验过程,并且不容易获得了解如何处理这些特定情况所需的较低级别的细节。

尽管如此,即使我使用 System.gc(),我仍然继续使用命令行参数进行调整,并设法将我的应用程序的整体处理时间提高了相对显着的数量,尽管无法克服处理更大的数据块造成的绊脚石。话虽如此,System.gc() 是一个工具....一个非常不可靠的工具,如果您不小心使用它,您会希望它不会经常工作。


S
Shervin Asgari

如果您想知道您的 System.gc() 是否被调用,您可以使用新的 Java 7 update 4 在 JVM 执行垃圾收集时获得通知。

我不是 100% 肯定 GarbageCollectorMXBean 类是在 Java 7 更新 4 中引入的,因为我在发行说明中找不到它,但我在 javaperformancetuning.com 站点中找到了信息


z
zxcv

当运行显式 GC 很好时,我想不出一个具体的例子。

一般来说,运行显式 GC 实际上弊大于利,因为显式 gc 会触发完整的收集,因为它遍历每个对象需要更长的时间。如果这个显式 gc 最终被重复调用,它很容易导致应用程序运行缓慢,因为需要花费大量时间来运行完整的 GC。

或者,如果使用堆分析器检查堆并且您怀疑库组件正在调用显式 GC,则可以将其关闭,并将:gc=-XX:+DisableExplicitGC 添加到 JVM 参数。


我们调用 System.gc() 来尝试关闭 MappedByteBuffer 对象,否则这些对象不会关闭,因此对于其他进程或文件截断不可用,即使我们在对象上调用了“close()”。不幸的是,它仍然不总是关闭它们。
A
Asterisk

根据 Bruce Eckel 在 Java 中的思考,显式 System.gc() 调用的一个用例是当您想要强制完成时,即调用 finalize 方法。


注意:有一个自己的方法可以强制完成运行。 (问题实际上不在于运行终结器,而是认识到一个对象可以被终结,因为它没有被引用)
f
fleture

当 system.gc 工作时,它将停止世界:所有响应都停止,因此垃圾收集器可以扫描每个对象以检查是否需要删除它。如果应用程序是一个 Web 项目,所有请求都会停止,直到 gc 完成,这将导致您的 Web 项目暂时无法工作。