使用gc()命令在R中强制进行垃圾回收

74

我时常在编程时草率行事,这会导致内存错误的出现。为了解决这个问题,我开始通过使用rm()命令删除对象来进行一些纪律性的训练,并且情况得到了改善。然而,在网上,有关于是否应该在删除大数据对象后显式调用gc()的混乱信息。有些人说在R返回内存错误之前它会自动运行gc(),而另一些人则认为手动强制调用gc()是个好主意。

我需要在删除大数据对象后调用gc()以确保最大化的内存可用性吗?

6个回答

51

"可能吧。" 我也这样做,甚至经常在循环中这样做,就像

cleanMem <- function(n=10) { for (i in 1:n) gc() }

但在我的经验中,这并不能将内存恢复到原始状态。

所以我通常会将需要处理的任务保存在脚本文件中,并使用“r”前端(在Unix上,或者使用“littler”包)执行这些脚本。在另一个操作系统上,可以使用Rscript作为替代品。

这种工作流程与以下相关:

我们之前在这里讨论过。


48
为什么反复运行 gc() 有帮助呢? - samhiggins2001
@DirkEddelbuettel - 为什么要反复运行 gc() - Chetan Arvind Patil
4
有没有任何一个由R支持但默认不带有Rscript的平台?我认为这是在任何操作系统上的替代选择,而不仅仅是“那个”平台。 - r2evans
1
“Rscript” 随处可见,而 “littler” 和它的 “r” 却不是。这就是上下文,我更喜欢后者(但前者只是修复了一个重要的错误)。 - Dirk Eddelbuettel

24

gc帮助页面得知:

 

调用 'gc' 会导致进行垃圾回收。   这将在没有用户干预的情况下自动进行,   调用 'gc' 的主要目的是报告内存使用情况。

    

但是,当大型对象被移除后,   调用 'gc' 可能是有用的,因为这可能会提示R将内存释放给操作系统。

所以做这个操作有时候是有用的,但大多数情况下不需要这样做。我的个人观点是,这是最后的手段 - 你不应该在代码中随处添加gc()语句,但如果你的计算机一直崩溃,并且你尝试了其他所有方法,那么它可能是有帮助的。

在尝试过其他一切之后,我指的是像以下这些方法:

  1. 编写函数而不是原始脚本,让变量超出范围。

  2. 如果您从一个与另一个无关的问题转移,请清空工作区。

  3. 丢弃您不感兴趣的数据/变量。(我经常收到带有数十个无趣列的电子表格。)


7
在我的电脑上,使用 gc() 可以释放一些内存,但不完美。 如果我加载一个大的对象,对它进行操作,删除它,然后使用 gc(),我得到的空闲内存与开始时不同。 我做的事情越多,就无法恢复更多的内存。最终,在处理许多大对象的操作之后,我可能会耗尽内存。 我使用 Windows 10 x64,并且有16GB的RAM。 - skan

15

有点晚,但还是想说:

显式调用gc会立即释放一些内存。……如果其他进程需要这些内存,这可能是一个好主意。例如,在调用system或类似函数之前。或者在脚本完成并且 R 将闲置一段时间直到下一个任务到达时,这样其他进程可以获得更多内存。

如果你只是想让你的脚本运行得更快,那么这并不重要,因为如果R需要它,它后面会自动调用。这甚至可能会更慢,因为正常的GC循环可能从未需要调用它。

……但如果你想测量时间,通常在运行测试之前进行GC是个好主意。这也是system.time默认执行的操作。

更新 正如@DWin所指出的,R(或C#、Java等)并不总是知道内存何时低于阈值需要运行GC。因此,有时候需要执行GC以解决内存系统中的缺陷。


14

据说R只使用RAM,但这在Mac上并不是真的(我怀疑Windows也是如此)。当RAM用尽时,它将开始使用虚拟内存。有时候,但并不总是,进程会“意识到”他们需要运行gc()并释放内存。如果他们没有这么做,你可以通过使用ActivityMonitor.app看到所有RAM都被占用且磁盘访问量已经增加。我发现,在进行大型Cox回归运算时,通过在调用之前使用gc(); cph(...),可以避免溢出到虚拟内存(慢速磁盘访问)。


我可以确认R在Windows上不使用页面文件,有时这会非常有用。 - skan

12

不需要。如果操作所需的内存不足,R会自动运行gc()


6
根据我的经验,这并不总是自动发生的。如果你经常使用大量数据,需要定期运行gc()或重启R会话。 - Zach
1
请提供您的陈述证据。 - hadley
7
V1 <- vector(length=208000000) sapply(1:20, function(x) {V2 <- vector(length=52000000); rm(V2); gc(); 0} )注:上述代码在 R 语言中运行,第一行创建一个长度为208000000的向量 V1,第二行循环20次创建长度为52000000的向量 V2,并删除 V2 变量以释放内存。代码最后输出一个长度为20的全零向量。运行过程中产生了32个内存分配警告。 - Zach
8
这是一个固定内存的情况。如果你使用rm()和gc(),就不会有问题。你可以增加内存限制来避免这个问题,但当好好的RAM可用时,R却开始使用swap真的很让人烦恼。 - Zach
R是否“知道”其他进程的内存页面必须被交换以提供所需的空间给R?因为根据我的经验,有时在中间步骤之后调用gc()可以帮助其他进程再次使用RAM,使计算机比调用gc()之前更加响应。 - hugovdberg
不管你在其他地方读到了什么,其实没有必要自己调用gc()。R会在需要更多空间时自动运行垃圾回收。 - Andrii

8
"也许吧。"我并没有一个明确的答案。但帮助文件建议只有两个原因需要调用gc():
  1. 你想要一份内存使用报告。
  2. 在删除大对象后,“它可能提示R将内存返回给操作系统。”
由于反复调用可能会减缓大型模拟的速度,因此我倾向于在删除大型对象后才这样做。换句话说,除非你有充分的理由,否则没有必要定期调用它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接