【问题标题】:Why does gc() not free memory?为什么 gc() 不释放内存?
【发布时间】:2013-01-12 20:46:36
【问题描述】:

我在具有 64 GB RAMWindows 64 位计算机 上运行模拟。内存使用达到 55%,在完成模拟运行后,我删除了工作空间中的所有对象 rm(list=ls()),然后是 double gc()

我以为这会为下一次模拟运行释放足够的内存,但实际上内存使用仅下降了 1%。咨询了很多不同的论坛我找不到满意的解释,只有模糊的cmets,例如:

“根据您的操作系统,释放的内存可能不会返回给操作系统,而是保留在进程空间中。”

我想查找以下信息:

  • 1) 哪个操作系统以及在何种条件下释放的内存不会返回给操作系统,以及
  • 2) 除了关闭 R 并重新启动它以进行下一次模拟运行之外,是否还有其他补救措施?

【问题讨论】:

  • 如果不关闭R,下次运行会不会内存不足?
  • 你以后真的会耗尽内存吗?
  • 我还不能检查。我对当前的项目非常着急,我不想面对模拟卡住的风险(他们需要六小时到两天)。
  • 这就是为什么在执行整个模拟之前在一个不错的小数据集上测试 gcrm... 的行为。

标签: r garbage-collection


【解决方案1】:

R 垃圾收集器在以下(并非如此)微妙的方面是不完美的:它确实移动对象(即,它不压缩内存),因为它与C 库的交互方式。 (一些other 语言/实现也受到此问题的影响,但others,尽管还必须与C 交互,但设法拥有compacting generational GC 确实不会 遇到这个问题) .

这意味着,如果您轮流分配小块内存,然后将其丢弃,而将较大块分配给更永久的对象(这是进行字符串/正则表达式处理时的常见情况),那么您的内存将变为 fragmented 和垃圾收集器对此无能为力:内存已释放,但不能重新使用,因为空闲块太短。

解决问题的唯一方法是保存你想要的对象,重启R,然后重新加载对象。

由于您正在做rm(list=ls()),即您不需要任何对象,您不需要保存和重新加载任何内容,因此,在您的情况下,解决方案正是您想要避免的 - 重新启动R .

PS1。垃圾收集是一个非常重要的话题。例如,Ruby used 5 (!) different GC algorithms over 20 years。 Java GC 并不糟糕,因为 Sun/OracleIBM 在各自的 GC 实现上花费了许多程序员年。另一方面,R 和 Python 的 GC 很糟糕——因为没有人费心投入必要的人工年限——而且它们很受欢迎。那是给你的worse-is-better

PS2。相关:R: running out of memory using `strsplit`

【讨论】:

  • @AlexanderHanysz:一点也不。唉,可靠地清理内存的唯一方法是重启 R。散布在已释放内存中的对象可能是工作环境的一部分,rm(list=ls()) 没有删除。
  • 感谢您的回复。这是非常不直观的!你能举出rm(list=ls())没有删除的对象的例子吗?
  • “解决问题的唯一方法是保存您想要的对象,重新启动 R,然后重新加载对象。”我认为这对 R 来说并不好。
  • @skan:R 非常擅长它的工作,它的 GC 极不可能被替换。 Python 的 GC 也很糟糕(它在引用计数!)我对 Julia 一无所知。一般来说,GC对于像R这样的研究平台来说是次要的。对于经常用于生产的Python来说更重要,但我认为可能会有变化。
  • 很好的答案。它甚至比你暗示的还要极端:“许多程序员年”是一个惊人的轻描淡写(实际数字到现在是几个世纪),而且他们的 GC 在某些情况下仍然很糟糕。
【解决方案2】:

如何检查内存使用情况?通常,虚拟机会分配一些内存块用于存储其数据。一些分配的可能未使用并标记为空闲。 GC 所做的是发现未从其他任何地方引用的数据并将相应的内存块标记为未使用,这并不意味着该内存已释放给操作系统。仍然从 VM 的角度来看,现在有更多可用内存可用于进一步计算。

正如其他人所问的那样,您是否遇到过内存不足的错误?如果没有,那就没什么好担心的了。

编辑: Thisthis 应该足以理解 R 中的内存分配和垃圾回收是如何工作的。

从第一个文档开始:

有时会尝试将未使用的页面释放回 操作系统。当页面被释放时,一些空闲节点 等于 R_MaxKeepFrac 乘以每个分配的节点数 类被保留。不需要满足此要求的页面是 释放。每个 R_PageReleaseFreq 级别 1 都会尝试释放页面 或 2 级集合。

EDIT2:

要查看已用内存,请尝试运行 gc(),并将详细设置为 TRUE:

gc(verbose=T)

这是内存中包含 10'000'000 个整数的数组的结果:

Garbage collection 9 = 1+0+8 (level 2) ... 
10.7 Mbytes of cons cells used (49%)
40.6 Mbytes of vectors used (72%)
          used (Mb) gc trigger (Mb) max used (Mb)
Ncells  198838 10.7     407500 21.8   350000 18.7
Vcells 5311050 40.6    7421749 56.7  5311504 40.6

在放弃对它的引用之后:

Garbage collection 10 = 1+0+9 (level 2) ... 
10.7 Mbytes of cons cells used (49%)
2.4 Mbytes of vectors used (5%)
         used (Mb) gc trigger (Mb) max used (Mb)
Ncells 198821 10.7     407500 21.8   350000 18.7
Vcells 310987  2.4    5937399 45.3  5311504 40.6

如您所见,Vcell 使用的内存从 40.6Mb 下降到 2.4Mb。

【讨论】:

  • 我使用 Windows 任务管理器检查内存使用情况。
  • @user7417 R 进程使用的任务管理器中显示的内存可以在 VM 级别上标记为空闲,这意味着所有这些都可以用于未来的计算。 GC 在执行 1 级或 2 级垃圾收集时可能会决定将其中的一部分释放给系统以让其他进程使用它。
  • 完成我的分析后,我检查了我是否真的会耗尽内存 - - 我没有(尽管 Windows 任务管理器显示最大部分内存仍然被占用)。所以我最好相信我的 gc()-输出...
  • 在我的电脑(Windows 10 12GB RAM)中,垃圾收集器的工作也很糟糕。如果我长时间使用大型数据集,无论我是否使用 gc(),所有 Windows 内存都会被填满,并且计算机会变得非常缓慢且无法使用。
猜你喜欢
  • 1970-01-01
  • 2019-02-13
  • 2014-12-27
  • 1970-01-01
  • 1970-01-01
  • 2017-02-12
  • 2016-02-23
  • 1970-01-01
相关资源
最近更新 更多