模块 ObjectSpace
ObjectSpace
模块包含一些与垃圾收集机制交互的例程,并允许您使用迭代器遍历所有存活对象。
ObjectSpace
还提供对对象终结器的支持,终结器是当特定对象即将被垃圾收集销毁时将被调用的 proc。有关如何正确使用此方法的重要信息,请参阅 ObjectSpace.define_finalizer
的文档。
a = "A" b = "B" ObjectSpace.define_finalizer(a, proc {|id| puts "Finalizer one on #{id}" }) ObjectSpace.define_finalizer(b, proc {|id| puts "Finalizer two on #{id}" }) a = nil b = nil
产生
Finalizer two on 537763470 Finalizer one on 537763480
公共类方法
统计所有按类型分组的对象。
它返回一个哈希,例如
{ :TOTAL=>10000, :FREE=>3011, :T_OBJECT=>6, :T_CLASS=>404, # ... }
返回的哈希内容是实现特定的。它可能会在将来发生变化。
以 :T_
开头的键表示存活对象。例如,:T_ARRAY
是数组的数量。:FREE
表示当前未使用的对象槽。:TOTAL
表示上述的总和。
如果给出了可选参数 result_hash
,它将被覆盖并返回。这旨在避免探测效应。
h = {} ObjectSpace.count_objects(h) puts h # => { :TOTAL=>10000, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249 }
此方法仅预期在 C Ruby 上工作。
static VALUE count_objects(int argc, VALUE *argv, VALUE os) { rb_objspace_t *objspace = &rb_objspace; size_t counts[T_MASK+1]; size_t freed = 0; size_t total = 0; size_t i; VALUE hash = Qnil; if (rb_check_arity(argc, 0, 1) == 1) { hash = argv[0]; if (!RB_TYPE_P(hash, T_HASH)) rb_raise(rb_eTypeError, "non-hash given"); } for (i = 0; i <= T_MASK; i++) { counts[i] = 0; } for (i = 0; i < heap_allocated_pages; i++) { struct heap_page *page = heap_pages_sorted[i]; short stride = page->slot_size; uintptr_t p = (uintptr_t)page->start; uintptr_t pend = p + page->total_slots * stride; for (;p < pend; p += stride) { VALUE vp = (VALUE)p; GC_ASSERT((NUM_IN_PAGE(vp) * BASE_SLOT_SIZE) % page->slot_size == 0); void *poisoned = asan_unpoison_object_temporary(vp); if (RANY(p)->as.basic.flags) { counts[BUILTIN_TYPE(vp)]++; } else { freed++; } if (poisoned) { GC_ASSERT(BUILTIN_TYPE(vp) == T_NONE); asan_poison_object(vp); } } total += page->total_slots; } if (NIL_P(hash)) { hash = rb_hash_new(); } else if (!RHASH_EMPTY_P(hash)) { rb_hash_stlike_foreach(hash, set_zero, hash); } rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total)); rb_hash_aset(hash, ID2SYM(rb_intern("FREE")), SIZET2NUM(freed)); for (i = 0; i <= T_MASK; i++) { VALUE type = type_sym(i); if (counts[i]) rb_hash_aset(hash, type, SIZET2NUM(counts[i])); } return hash; }
将 aProc 添加为终结器,在 obj 被销毁后调用。obj 的对象 ID 将作为参数传递给 aProc。如果 aProc 是一个 lambda 或方法,请确保它可以接受一个参数调用。
返回值是一个数组 [0, aProc]
。
两种推荐的模式是在非实例方法中创建终结器 proc,在该方法中可以安全地捕获所需的状态,或者使用自定义可调用对象,该对象将所需的状态显式存储为实例变量。
class Foo def initialize(data_needed_for_finalization) ObjectSpace.define_finalizer(self, self.class.create_finalizer(data_needed_for_finalization)) end def self.create_finalizer(data_needed_for_finalization) proc { puts "finalizing #{data_needed_for_finalization}" } end end class Bar class Remover def initialize(data_needed_for_finalization) @data_needed_for_finalization = data_needed_for_finalization end def call(id) puts "finalizing #{@data_needed_for_finalization}" end end def initialize(data_needed_for_finalization) ObjectSpace.define_finalizer(self, Remover.new(data_needed_for_finalization)) end end
请注意,如果您的终结器引用了要终结的对象,它将永远不会在 GC
上运行,尽管它仍然会在退出时运行。如果您将要终结的对象捕获为终结器的接收者,您将收到警告。
class CapturesSelf def initialize(name) ObjectSpace.define_finalizer(self, proc { # this finalizer will only be run on exit puts "finalizing #{name}" }) end end
还要注意,终结可能是不可预测的,并且除了在退出时之外,永远不能保证运行。
static VALUE define_final(int argc, VALUE *argv, VALUE os) { VALUE obj, block; rb_scan_args(argc, argv, "11", &obj, &block); should_be_finalizable(obj); if (argc == 1) { block = rb_block_proc(); } else { should_be_callable(block); } if (rb_callable_receiver(block) == obj) { rb_warn("finalizer references object to be finalized"); } return rb_define_finalizer_no_check(obj, block); }
在当前 Ruby 进程中,对每个存活的非立即对象调用一次块。如果指定了module,则仅对与module匹配(或为其子类)的类或模块调用块。返回找到的对象数量。立即对象(Fixnum
、Symbol
、true
、false
和 nil
)永远不会返回。在下面的示例中,each_object 返回我们定义的数字和在 Math
模块中定义的几个常量。
如果没有给出块,则返回一个枚举器。
a = 102.7 b = 95 # Won't be returned c = 12345678987654321 count = ObjectSpace.each_object(Numeric) {|x| p x } puts "Total count: #{count}"
产生
12345678987654321 102.7 2.71828182845905 3.14159265358979 2.22044604925031e-16 1.7976931348623157e+308 2.2250738585072e-308 Total count: 7
static VALUE os_each_obj(int argc, VALUE *argv, VALUE os) { VALUE of; of = (!rb_check_arity(argc, 0, 1) ? 0 : argv[0]); RETURN_ENUMERATOR(os, 1, &of); return os_obj_of(of); }
GC.start
的别名
# File ruby_3_3_0/gc.rb, line 327 def garbage_collect full_mark: true, immediate_mark: true, immediate_sweep: true Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false end
删除obj的所有终结器。
static VALUE undefine_final(VALUE os, VALUE obj) { return rb_undefine_finalizer(obj); }
私有实例方法
GC.start
的别名
# File ruby_3_3_0/gc.rb, line 327 def garbage_collect full_mark: true, immediate_mark: true, immediate_sweep: true Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false end