【问题标题】:Anonymous modules and classes garbage collection in RubyRuby 中的匿名模块和类垃圾收集
【发布时间】:2013-08-26 12:44:10
【问题描述】:

我想知道为什么下面的代码显然不会垃圾收集据称不再在任何地方引用的匿名模块(未扩展/包含,未命名,包含设置为 nil 的数组)。

如果有人能用相对简单/通用的编程语言来阐明幕后发生的事情,我将不胜感激。有没有一种特殊的红宝石方法来实现这一点?无论如何都不能对匿名模块/类进行垃圾收集吗?还是我只是被我得到的内存统计数据误导了?

注意:我使用的是 ruby​​ 1.9.3;不知道 ruby​​ 2.x 是否会改变任何东西...... 注意 2:无论模块是否定义 foo 方法,结果都是相同的

提前致谢。

puts("INITIAL OBJECT SPACE OBJECTS : #{ObjectSpace.count_objects}")


i = 100000
ms = []

i.times do
    ms << Module.new do
        def foo()
             puts('foo method called')
        end
    end
end

puts("#{i} MODULES CREATED")
puts("OBJECT SPACE OBJECTS : #{ObjectSpace.count_objects}")

ms = nil
ObjectSpace.garbage_collect

puts("#{i} MODULES GARBAGE COLLECTED")
puts("WAITING TO END PROGRAM")

stop = gets
puts("FINAL OBJECT SPACE OBJECTS : #{ObjectSpace.count_objects}")

我说“显然没有垃圾收集”,因为我的操作系统任务管理器没有显示该进程的内存使用量有任何减少,并且调用 ObjectSpace.count_objects 会产生以下内容,我读到(错误地如此?)为:没有你的模块使用的内存没有被释放。

初始对象空间对象:{:TOTAL=>14730, :FREE=>251, :T_OBJECT=>8, :T_CLASS=>542, :T_MODULE=>21, :T_FLOAT=>7, :T_STRING=>6349 , :T_REGEXP=>24, :T_ARRAY=>1009, :T_HASH=>14, :T_BIGNUM=>3, :T_FILE=>10, :T_DATA=>408, :T_MATCH=>108, :T_COMPLEX=>1, : T_NODE=>5956, :T_ICLASS=>19}

已创建 100000 个模块

对象空间对象:{:TOTAL=>311794, :FREE=>59829, :T_OBJECT=>6, :T_CLASS=>542, :T_MODULE=>100021, :T_FLOAT=>7, :T_STRING=>3332, :T_REGEXP=>22, :T_ARRAY=>23868, :T_HASH=>10, :T_BIGNUM=>3, :T_FILE=>3, :T_DATA=>100324, :T_COMPLEX=>1, :T_NODE=>23807, :T_ICLASS =>19}

100000 模块垃圾收集 等待结束程序

最终对象空间对象:{:TOTAL=>311794, :FREE=>107155, :T_OBJECT=>6, :T_CLASS=>542, :T_MODULE=>100021, :T_FLOAT=>7, :T_STRING=>3335 , :T_REGEXP=>22, :T_ARRAY=>203, :T_HASH=>10, :T_BIGNUM=>3, :T_FILE=>3, :T_DATA=>100324, :T_COMPLEX=>1, :T_NODE=>143, : T_ICLASS=>19}

【问题讨论】:

  • 试试这个ruby-doc.org/core-1.9.3/GC.html#method-c-start 而不是ObjectSpace.garbage_collect。请告诉我。
  • 对我来说,它产生相同的值 21 - 31 - 31,使用您下面答案中的代码示例(请参阅那里的评论)。

标签: ruby class module garbage-collection anonymous-class


【解决方案1】:

调用GC.startObjectSpace.garbage_collect 并不意味着将执行垃圾回收。这只是对 Ruby 运行时的一个提示。

在某些平台上,Ruby 运行时甚至不可能启动垃圾收集,因为垃圾收集器甚至不是 Ruby 运行时的一部分,例如在 JRuby 或 IronRuby 上。

一般来说,Ruby 运行时会自行决定何时执行垃圾回收。通常,那是它耗尽内存的时候。 100000 个模块并没有那么大,所以根本不需要执行 GC 循环。

此外,大多数 Ruby 实现永远不会将内存释放回操作系统,即使在 GC 周期之后也是如此。

所以,ObjectSpace 没有缩小这一事实并不一定意味着您有内存泄漏。这可能只是意味着 Ruby 运行时尚未认为有必要运行 GC 循环。

还请注意,如果您从 IRb、Pry、IDE 控制台或其他一些非标准环境运行此代码,则可能会出现 那些 保留其中一些模块的情况。例如,Pry 将最后 100 条命令的结果存储在历史数组中。 (输入示例程序后,尝试在 Pry 中评估 _out_[5]。)

【讨论】:

  • 是的,我的回答 GC.start 部分工作,我可以通过查看 puts "FINAL OBJECT SPACE OBJECTS : #{ObjectSpace.count_objects[:T_MODULE]}" 的输出来理解。
  • 很高兴没有我无法避免的“结构性”内存泄漏。我所做的是相当实验性的,所以我不介意何时或即使内存被释放,只要它被认为是被释放的。因此,如果您说无论 ruby​​ 实现如何,一个未引用的匿名模块都符合 GC 的条件,那么就我而言,您的答案是可以接受的。非常感谢。
【解决方案2】:

你可以查看GC#start的方法

puts "INITIAL OBJECT SPACE OBJECTS : #{ObjectSpace.count_objects[:T_MODULE]}"
i = 10
ms = []

i.times do
    ms << Module.new do
        def foo()
             puts('foo method called')
        end
    end
end

puts "#{i} MODULES CREATED" 
puts "OBJECT SPACE OBJECTS : #{ObjectSpace.count_objects[:T_MODULE]}"


p ms
ms = nil
GC.start

puts "#{i} MODULES GARBAGE COLLECTED"
puts "WAITING TO END PROGRAM"
p ms 
puts "FINAL OBJECT SPACE OBJECTS : #{ObjectSpace.count_objects[:T_MODULE]}"

输出:

INITIAL OBJECT SPACE OBJECTS : 21
10 MODULES CREATED
OBJECT SPACE OBJECTS : 31
[#<Module:0xe33a48>, #<Module:0xe338c8>, #<Module:0xe33808>, #<Module:0xe337a8>, #<Module:0xe33610>, 
#<Module:0xe334c0>, #<Module:0xe33328>, #<Module:0xe33250>, #<Module:0xe33118>, #<Module:0xe33088>]
10 MODULES GARBAGE COLLECTED
WAITING TO END PROGRAM
nil
FINAL OBJECT SPACE OBJECTS : 23

所以你可以看到垃圾回收后我得到的最终计数是 23 而不是 21,这正是 @Jörg W Mittag 解释的。

【讨论】:

  • 使用您的代码 T_MODULE 计数最终得到相同的 31,而不是 23。这是 ruby​​ 实现的地方吗?感谢您的帮助,但我不会搞乱 GC 和 ObjectSpace。如上所述,我只是想确保模块和类是真正的普通对象,因此是 GCable。
  • 出于好奇,这 2 个额外的项目 (23 - 21 = 2) 是从哪里来的?
  • @ergosumcogito 我不认为有任何额外的结果出现......正如 Jorg 提到的那样,回收并没有像我们期望的那样在 ruby​​ 中发生......可能就是这个原因......
  • ~ offtopic : 是的,我记得有一篇博文说不要用 ruby​​ 开发游戏,因为 GC 的状态。他们说回收有其局限性...... :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多