модуль ObjectSpace
Библиотека objspace расширяет модуль ObjectSpace и добавляет несколько методов для получения внутренней статистической информации об управлении объектами/памятью.
Вам необходимо require 'objspace' для использования этого расширяемого модуля.
В общем случае, вы *НЕ ДОЛЖНЫ* использовать эту библиотеку, если не знаете о реализации MRI. В основном, эта библиотека предназначена для разработчиков профилировщиков памяти и разработчиков MRI, которым необходимо знать об использовании памяти MRI.
Модуль ObjectSpace содержит ряд процедур, которые взаимодействуют с механизмом сбора мусора и позволяют вам перебирать все живые объекты с помощью итератора.
ObjectSpace также предоставляет поддержку для финализаторов объектов, процедур, которые будут вызваны, когда конкретный объект собирается быть уничтожен сбором мусора. См. документацию по 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
Публичные методы класса
static VALUE
objspace_dump(VALUE os, VALUE obj, VALUE output)
{
struct dump_config dc = {0,};
dump_output(&dc, output, Qnil, Qnil);
dump_object(obj, &dc);
return dump_result(&dc);
} static VALUE
objspace_dump_all(VALUE os, VALUE output, VALUE full, VALUE since)
{
struct dump_config dc = {0,};
dump_output(&dc, output, full, since);
if (!dc.partial_dump || dc.since == 0) {
/* dump roots */
rb_objspace_reachable_objects_from_root(root_obj_i, &dc);
if (dc.roots) dump_append(&dc, "]}\n");
}
/* dump all objects */
rb_objspace_each_objects(heap_i, &dc);
return dump_result(&dc);
} static VALUE
os_id2ref(VALUE os, VALUE objid)
{
return id2ref(objid);
} static VALUE
allocation_class_path(VALUE self, VALUE obj)
{
struct allocation_info *info = lookup_allocation_info(obj);
if (info && info->class_path) {
return rb_str_new2(info->class_path);
}
else {
return Qnil;
}
} Возвращает класс для заданного object.
class A
def foo
ObjectSpace::trace_object_allocations do
obj = Object.new
p "#{ObjectSpace::allocation_class_path(obj)}"
end
end
end
A.new.foo #=> "Class"
См. ::trace_object_allocations для получения дополнительной информации и примеров.
static VALUE
allocation_generation(VALUE self, VALUE obj)
{
struct allocation_info *info = lookup_allocation_info(obj);
if (info) {
return SIZET2NUM(info->generation);
}
else {
return Qnil;
}
} Возвращает поколение сборщика мусора для заданного object.
class B
include ObjectSpace
def foo
trace_object_allocations do
obj = Object.new
p "Generation is #{allocation_generation(obj)}"
end
end
end
B.new.foo #=> "Generation is 3"
См. ::trace_object_allocations для получения дополнительной информации и примеров.
static VALUE
allocation_method_id(VALUE self, VALUE obj)
{
struct allocation_info *info = lookup_allocation_info(obj);
if (info) {
return info->mid;
}
else {
return Qnil;
}
} Возвращает идентификатор метода для заданного object.
class A
include ObjectSpace
def foo
trace_object_allocations do
obj = Object.new
p "#{allocation_class_path(obj)}##{allocation_method_id(obj)}"
end
end
end
A.new.foo #=> "Class#new"
См. ::trace_object_allocations для получения дополнительной информации и примеров.
static VALUE
allocation_sourcefile(VALUE self, VALUE obj)
{
struct allocation_info *info = lookup_allocation_info(obj);
if (info && info->path) {
return rb_str_new2(info->path);
}
else {
return Qnil;
}
} Возвращает исходный файл, из которого произошел данный object.
См. ::trace_object_allocations для получения дополнительной информации и примеров.
static VALUE
allocation_sourceline(VALUE self, VALUE obj)
{
struct allocation_info *info = lookup_allocation_info(obj);
if (info) {
return INT2FIX(info->line);
}
else {
return Qnil;
}
} Возвращает исходную строку из исходного кода для данного object.
См. ::trace_object_allocations для получения дополнительной информации и примеров.
static VALUE
count_imemo_objects(int argc, VALUE *argv, VALUE self)
{
VALUE hash = setup_hash(argc, argv);
if (imemo_type_ids[0] == 0) {
imemo_type_ids[0] = rb_intern("imemo_env");
imemo_type_ids[1] = rb_intern("imemo_cref");
imemo_type_ids[2] = rb_intern("imemo_svar");
imemo_type_ids[3] = rb_intern("imemo_throw_data");
imemo_type_ids[4] = rb_intern("imemo_ifunc");
imemo_type_ids[5] = rb_intern("imemo_memo");
imemo_type_ids[6] = rb_intern("imemo_ment");
imemo_type_ids[7] = rb_intern("imemo_iseq");
imemo_type_ids[8] = rb_intern("imemo_tmpbuf");
imemo_type_ids[9] = rb_intern("imemo_ast");
imemo_type_ids[10] = rb_intern("imemo_parser_strterm");
imemo_type_ids[11] = rb_intern("imemo_callinfo");
imemo_type_ids[12] = rb_intern("imemo_callcache");
imemo_type_ids[13] = rb_intern("imemo_constcache");
}
each_object_with_flags(count_imemo_objects_i, (void *)hash);
return hash;
} Подсчитывает объекты для каждого типа T_IMEMO.
Этот метод предназначен только для разработчиков MRI, заинтересованных в производительности и использовании памяти программ на Ruby.
Он возвращает хэш в виде:
{:imemo_ifunc=>8,
:imemo_svar=>7,
:imemo_cref=>509,
:imemo_memo=>1,
:imemo_throw_data=>1}
Если задан необязательный аргумент result_hash, он перезаписывается и возвращается. Это предназначено для предотвращения эффекта зондирования.
Содержимое возвращаемого хэша является специфичным для реализации и может измениться в будущем.
В этой версии ключи являются объектами-символами.
Ожидается, что этот метод будет работать только с C Ruby.
static VALUE
count_nodes(int argc, VALUE *argv, VALUE os)
{
size_t nodes[NODE_LAST+1];
enum node_type i;
VALUE hash = setup_hash(argc, argv);
for (i = 0; i <= NODE_LAST; i++) {
nodes[i] = 0;
}
each_object_with_flags(cn_i, &nodes[0]);
for (i=0; i<NODE_LAST; i++) {
if (nodes[i] != 0) {
VALUE node;
switch (i) {
#define COUNT_NODE(n) case n: node = ID2SYM(rb_intern(#n)); goto set
COUNT_NODE(NODE_SCOPE);
COUNT_NODE(NODE_BLOCK);
COUNT_NODE(NODE_IF);
COUNT_NODE(NODE_UNLESS);
COUNT_NODE(NODE_CASE);
COUNT_NODE(NODE_CASE2);
COUNT_NODE(NODE_CASE3);
COUNT_NODE(NODE_WHEN);
COUNT_NODE(NODE_IN);
COUNT_NODE(NODE_WHILE);
COUNT_NODE(NODE_UNTIL);
COUNT_NODE(NODE_ITER);
COUNT_NODE(NODE_FOR);
COUNT_NODE(NODE_FOR_MASGN);
COUNT_NODE(NODE_BREAK);
COUNT_NODE(NODE_NEXT);
COUNT_NODE(NODE_REDO);
COUNT_NODE(NODE_RETRY);
COUNT_NODE(NODE_BEGIN);
COUNT_NODE(NODE_RESCUE);
COUNT_NODE(NODE_RESBODY);
COUNT_NODE(NODE_ENSURE);
COUNT_NODE(NODE_AND);
COUNT_NODE(NODE_OR);
COUNT_NODE(NODE_MASGN);
COUNT_NODE(NODE_LASGN);
COUNT_NODE(NODE_DASGN);
COUNT_NODE(NODE_GASGN);
COUNT_NODE(NODE_IASGN);
COUNT_NODE(NODE_CDECL);
COUNT_NODE(NODE_CVASGN);
COUNT_NODE(NODE_OP_ASGN1);
COUNT_NODE(NODE_OP_ASGN2);
COUNT_NODE(NODE_OP_ASGN_AND);
COUNT_NODE(NODE_OP_ASGN_OR);
COUNT_NODE(NODE_OP_CDECL);
COUNT_NODE(NODE_CALL);
COUNT_NODE(NODE_OPCALL);
COUNT_NODE(NODE_FCALL);
COUNT_NODE(NODE_VCALL);
COUNT_NODE(NODE_QCALL);
COUNT_NODE(NODE_SUPER);
COUNT_NODE(NODE_ZSUPER);
COUNT_NODE(NODE_LIST);
COUNT_NODE(NODE_ZLIST);
COUNT_NODE(NODE_VALUES);
COUNT_NODE(NODE_HASH);
COUNT_NODE(NODE_RETURN);
COUNT_NODE(NODE_YIELD);
COUNT_NODE(NODE_LVAR);
COUNT_NODE(NODE_DVAR);
COUNT_NODE(NODE_GVAR);
COUNT_NODE(NODE_IVAR);
COUNT_NODE(NODE_CONST);
COUNT_NODE(NODE_CVAR);
COUNT_NODE(NODE_NTH_REF);
COUNT_NODE(NODE_BACK_REF);
COUNT_NODE(NODE_MATCH);
COUNT_NODE(NODE_MATCH2);
COUNT_NODE(NODE_MATCH3);
COUNT_NODE(NODE_LIT);
COUNT_NODE(NODE_STR);
COUNT_NODE(NODE_DSTR);
COUNT_NODE(NODE_XSTR);
COUNT_NODE(NODE_DXSTR);
COUNT_NODE(NODE_EVSTR);
COUNT_NODE(NODE_DREGX);
COUNT_NODE(NODE_ONCE);
COUNT_NODE(NODE_ARGS);
COUNT_NODE(NODE_ARGS_AUX);
COUNT_NODE(NODE_OPT_ARG);
COUNT_NODE(NODE_KW_ARG);
COUNT_NODE(NODE_POSTARG);
COUNT_NODE(NODE_ARGSCAT);
COUNT_NODE(NODE_ARGSPUSH);
COUNT_NODE(NODE_SPLAT);
COUNT_NODE(NODE_BLOCK_PASS);
COUNT_NODE(NODE_DEFN);
COUNT_NODE(NODE_DEFS);
COUNT_NODE(NODE_ALIAS);
COUNT_NODE(NODE_VALIAS);
COUNT_NODE(NODE_UNDEF);
COUNT_NODE(NODE_CLASS);
COUNT_NODE(NODE_MODULE);
COUNT_NODE(NODE_SCLASS);
COUNT_NODE(NODE_COLON2);
COUNT_NODE(NODE_COLON3);
COUNT_NODE(NODE_DOT2);
COUNT_NODE(NODE_DOT3);
COUNT_NODE(NODE_FLIP2);
COUNT_NODE(NODE_FLIP3);
COUNT_NODE(NODE_SELF);
COUNT_NODE(NODE_NIL);
COUNT_NODE(NODE_TRUE);
COUNT_NODE(NODE_FALSE);
COUNT_NODE(NODE_ERRINFO);
COUNT_NODE(NODE_DEFINED);
COUNT_NODE(NODE_POSTEXE);
COUNT_NODE(NODE_DSYM);
COUNT_NODE(NODE_ATTRASGN);
COUNT_NODE(NODE_LAMBDA);
COUNT_NODE(NODE_ARYPTN);
COUNT_NODE(NODE_FNDPTN);
COUNT_NODE(NODE_HSHPTN);
#undef COUNT_NODE
case NODE_LAST: break;
}
UNREACHABLE;
set:
rb_hash_aset(hash, node, SIZET2NUM(nodes[i]));
}
}
return hash;
} Подсчитывает узлы для каждого типа узла.
Этот метод предназначен только для разработчиков MRI, заинтересованных в производительности и использовании памяти программ на Ruby.
Он возвращает хэш в виде:
{:NODE_METHOD=>2027, :NODE_FBODY=>1927, :NODE_CFUNC=>1798, ...} Если задан необязательный аргумент result_hash, он перезаписывается и возвращается. Это предназначено для предотвращения эффекта зондирования.
Примечание: Содержимое возвращаемого хэша определяется реализацией. Оно может быть изменено в будущем.
Ожидается, что этот метод будет работать только с 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) * sizeof(RVALUE)) % page->slot_size == 0);
void *poisoned = asan_poisoned_object_p(vp);
asan_unpoison_object(vp, false);
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;
} Подсчитывает все объекты, сгруппированные по типу.
Он возвращает хэш, например:
{
: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_size(int argc, VALUE *argv, VALUE os)
{
size_t counts[T_MASK+1];
size_t total = 0;
enum ruby_value_type i;
VALUE hash = setup_hash(argc, argv);
for (i = 0; i <= T_MASK; i++) {
counts[i] = 0;
}
each_object_with_flags(cos_i, &counts[0]);
for (i = 0; i <= T_MASK; i++) {
if (counts[i]) {
VALUE type = type2sym(i);
total += counts[i];
rb_hash_aset(hash, type, SIZET2NUM(counts[i]));
}
}
rb_hash_aset(hash, ID2SYM(rb_intern("TOTAL")), SIZET2NUM(total));
return hash;
} Подсчитывает размер объектов (в байтах) для каждого типа.
Обратите внимание, что эта информация неполна. Вам необходимо обращаться с этой информацией только как с ПОДСКАЗКОЙ. В частности, общий размер T_DATA может быть неверным.
Он возвращает хэш в виде:
{:TOTAL=>1461154, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249, ...} Если задан необязательный аргумент result_hash, он перезаписывается и возвращается. Это предназначено для предотвращения эффекта зондирования.
Содержимое возвращаемого хэша определяется реализацией. Оно может быть изменено в будущем.
Ожидается, что этот метод будет работать только с C Ruby.
static VALUE
count_symbols(int argc, VALUE *argv, VALUE os)
{
struct dynamic_symbol_counts dynamic_counts = {0, 0};
VALUE hash = setup_hash(argc, argv);
size_t immortal_symbols = rb_sym_immortal_count();
each_object_with_flags(cs_i, &dynamic_counts);
rb_hash_aset(hash, ID2SYM(rb_intern("mortal_dynamic_symbol")), SIZET2NUM(dynamic_counts.mortal));
rb_hash_aset(hash, ID2SYM(rb_intern("immortal_dynamic_symbol")), SIZET2NUM(dynamic_counts.immortal));
rb_hash_aset(hash, ID2SYM(rb_intern("immortal_static_symbol")), SIZET2NUM(immortal_symbols - dynamic_counts.immortal));
rb_hash_aset(hash, ID2SYM(rb_intern("immortal_symbol")), SIZET2NUM(immortal_symbols));
return hash;
} Подсчитывает символы для каждого типа Symbol.
Этот метод предназначен только для разработчиков MRI, заинтересованных в производительности и использовании памяти программ на Ruby.
Если задан необязательный аргумент result_hash, он перезаписывается и возвращается. Это предназначено для предотвращения эффекта зондирования.
Примечание: Содержимое возвращаемого хэша определяется реализацией. Оно может быть изменено в будущем.
Ожидается, что этот метод будет работать только с C Ruby.
В этой версии MRI у них есть 3 типа символов (и 1 общее количество).
* mortal_dynamic_symbol: GC target symbols (collected by GC) * immortal_dynamic_symbol: Immortal symbols promoted from dynamic symbols (do not collected by GC) * immortal_static_symbol: Immortal symbols (do not collected by GC) * immortal_symbol: total immortal symbols (immortal_dynamic_symbol+immortal_static_symbol)
static VALUE
count_tdata_objects(int argc, VALUE *argv, VALUE self)
{
VALUE hash = setup_hash(argc, argv);
each_object_with_flags(cto_i, (void *)hash);
return hash;
} Подсчитывает объекты для каждого типа T_DATA.
Этот метод предназначен только для разработчиков MRI, заинтересованных в производительности и использовании памяти программ на Ruby.
Он возвращает хэш в виде:
{RubyVM::InstructionSequence=>504, :parser=>5, :barrier=>6,
:mutex=>6, Proc=>60, RubyVM::Env=>57, Mutex=>1, Encoding=>99,
ThreadGroup=>1, Binding=>1, Thread=>1, RubyVM=>1, :iseq=>1,
Random=>1, ARGF.class=>1, Data=>1, :autoload=>3, Time=>2}
# T_DATA objects existing at startup on r32276.
Если задан необязательный аргумент result_hash, он перезаписывается и возвращается. Это предназначено для предотвращения эффекта зондирования.
Содержимое возвращаемого хэша является специфичным для реализации и может измениться в будущем.
В этой версии ключи являются объектом Class или объектом Symbol.
Если объект является обычным (доступным) объектом, ключом является объект Class. Если объект не является обычным (внутренним) объектом, ключом является имя символа, зарегистрированное с помощью rb_data_type_struct.
Ожидается, что этот метод будет работать только с C Ruby.
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 define_final0(obj, block);
} Добавляет aProc в качестве финализатора, который будет вызван после уничтожения obj. Идентификатор объекта obj будет передан в качестве аргумента aProc. Если aProc является лямбда-выражением или методом, убедитесь, что он может быть вызван с одним аргументом.
Возвращаемым значением является массив [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
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);
} Вызывает блок один раз для каждого активного, не мгновенного объекта в этом процессе Ruby. Если указан module, вызывает блок только для тех классов или модулей, которые совпадают (или являются подклассом) module. Возвращает количество найденных объектов. Мгновенные объекты (Fixnums, Symbols 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
# File gc.rb, line 306 def garbage_collect full_mark: true, immediate_mark: true, immediate_sweep: true Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false end
static VALUE
objspace_internal_class_of(VALUE self, VALUE obj)
{
VALUE klass;
if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
obj = (VALUE)DATA_PTR(obj);
}
if (RB_TYPE_P(obj, T_IMEMO)) {
return Qnil;
}
else {
klass = CLASS_OF(obj);
return wrap_klass_iow(klass);
}
} - Особенности MRI
-
Возвращает внутренний класс объекта obj.
obj может быть экземпляром InternalObjectWrapper.
Обратите внимание, что вы не должны использовать этот метод в своем приложении.
static VALUE
objspace_internal_super_of(VALUE self, VALUE obj)
{
VALUE super;
if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
obj = (VALUE)DATA_PTR(obj);
}
switch (OBJ_BUILTIN_TYPE(obj)) {
case T_MODULE:
case T_CLASS:
case T_ICLASS:
super = RCLASS_SUPER(obj);
break;
default:
rb_raise(rb_eArgError, "class or module is expected");
}
return wrap_klass_iow(super);
} obj может быть экземпляром InternalObjectWrapper.
Обратите внимание, что вы не должны использовать этот метод в своем приложении.
static VALUE
memsize_of_m(VALUE self, VALUE obj)
{
return SIZET2NUM(rb_obj_memsize_of(obj));
} Возвращает размер занимаемой памяти объектом obj в байтах.
Обратите внимание, что возвращаемый размер неполный. Вы должны рассматривать эту информацию как подсказку. В особенности, размер T_DATA может быть некорректным.
Этот метод ожидается работать только с C Ruby.
Начиная с Ruby 2.2, memsize_of(obj) возвращает размер памяти, включающий sizeof(RVALUE).
static VALUE
memsize_of_all_m(int argc, VALUE *argv, VALUE self)
{
struct total_data data = {0, 0};
if (argc > 0) {
rb_scan_args(argc, argv, "01", &data.klass);
}
each_object_with_flags(total_i, &data);
return SIZET2NUM(data.total);
} Возвращает суммарный размер занимаемой памяти всех активных объектов в байтах.
Если указан klass (должен быть объект Class), возвращает суммарный размер памяти экземпляров данного класса.
Обратите внимание, что возвращаемый размер неполный. Вы должны рассматривать эту информацию как подсказку. В особенности, размер T_DATA может быть некорректным.
Обратите внимание, что этот метод НЕ возвращает общий размер выделенной памяти.
Этот метод может быть определен следующим кодом Ruby:
def memsize_of_all klass = false
total = 0
ObjectSpace.each_object{|e|
total += ObjectSpace.memsize_of(e) if klass == false || e.kind_of?(klass)
}
total
end
Этот метод ожидается работать только с C Ruby.
static VALUE
reachable_objects_from(VALUE self, VALUE obj)
{
if (rb_objspace_markable_object_p(obj)) {
struct rof_data data;
if (rb_typeddata_is_kind_of(obj, &iow_data_type)) {
obj = (VALUE)DATA_PTR(obj);
}
data.refs = rb_ident_hash_new();
data.internals = rb_ary_new();
rb_objspace_reachable_objects_from(obj, reachable_object_from_i, &data);
return rb_funcall(data.refs, rb_intern("values"), 0);
}
else {
return Qnil;
}
} - Особенности MRI
-
Возвращает все доступные объекты от ‘obj’.
Этот метод возвращает все доступные объекты от ‘obj’.
Если ‘obj’ имеет две или более ссылки на один и тот же объект ‘x’, то возвращаемый массив содержит только один объект ‘x’.
Если ‘obj’ — это необъект-метка (неуправляемый кучей объект), например true, false, nil, символы и Fixnum (и Flonum), он просто возвращает nil.
Если ‘obj’ имеет ссылки на внутренний объект, то возвращаются экземпляры класса ObjectSpace::InternalObjectWrapper. Этот объект содержит ссылку на внутренний объект, и вы можете проверить тип внутреннего объекта с помощью метода ‘type’.
Если ‘obj’ является экземпляром класса ObjectSpace::InternalObjectWrapper, этот метод возвращает все доступные объекты из внутреннего объекта, на который указывает ‘obj’.
С помощью этого метода вы можете найти утечки памяти.
Этот метод ожидается работать только с C Ruby.
Пример:
ObjectSpace.reachable_objects_from(['a', 'b', 'c']) #=> [Array, 'a', 'b', 'c'] ObjectSpace.reachable_objects_from(['a', 'a', 'a']) #=> [Array, 'a', 'a', 'a'] # all 'a' strings have different object id ObjectSpace.reachable_objects_from([v = 'a', v, v]) #=> [Array, 'a'] ObjectSpace.reachable_objects_from(1) #=> nil # 1 is not markable (heap managed) object
static VALUE
reachable_objects_from_root(VALUE self)
{
struct rofr_data data;
VALUE hash = data.categories = rb_ident_hash_new();
data.last_category = 0;
rb_objspace_reachable_objects_from_root(reachable_object_from_root_i, &data);
rb_hash_foreach(hash, collect_values_of_values, hash);
return hash;
} - Особенности MRI
-
Возвращает все доступные объекты из корня.
static VALUE
trace_object_allocations(VALUE self)
{
trace_object_allocations_start(self);
return rb_ensure(rb_yield, Qnil, trace_object_allocations_stop, self);
} Начинает отслеживать выделение памяти из модуля расширения ObjectSpace.
Например:
require 'objspace'
class C
include ObjectSpace
def foo
trace_object_allocations do
obj = Object.new
p "#{allocation_sourcefile(obj)}:#{allocation_sourceline(obj)}"
end
end
end
C.new.foo #=> "objtrace.rb:8"
В этом примере был включён модуль ObjectSpace, чтобы сделать его легче читать, но вы также можете использовать запись ::trace_object_allocations (рекомендуется).
Обратите внимание, что эта функция приводит к значительному снижению производительности и большому потреблению памяти.
static VALUE
trace_object_allocations_clear(VALUE self)
{
struct traceobj_arg *arg = get_traceobj_arg();
/* clear tables */
st_foreach(arg->object_table, free_values_i, 0);
st_clear(arg->object_table);
st_foreach(arg->str_table, free_keys_i, 0);
st_clear(arg->str_table);
/* do not touch TracePoints */
return Qnil;
} Очищает записанную информацию отслеживания.
static VALUE
trace_object_allocations_debug_start(VALUE self)
{
tmp_keep_remains = 1;
if (object_allocations_reporter_registered == 0) {
object_allocations_reporter_registered = 1;
rb_bug_reporter_add(object_allocations_reporter, 0);
}
return trace_object_allocations_start(self);
} static VALUE
trace_object_allocations_start(VALUE self)
{
struct traceobj_arg *arg = get_traceobj_arg();
if (arg->running++ > 0) {
/* do nothing */
}
else {
if (arg->newobj_trace == 0) {
arg->newobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_NEWOBJ, newobj_i, arg);
arg->freeobj_trace = rb_tracepoint_new(0, RUBY_INTERNAL_EVENT_FREEOBJ, freeobj_i, arg);
}
rb_tracepoint_enable(arg->newobj_trace);
rb_tracepoint_enable(arg->freeobj_trace);
}
return Qnil;
} Начинает отслеживание выделения объектов.
static VALUE
trace_object_allocations_stop(VALUE self)
{
struct traceobj_arg *arg = get_traceobj_arg();
if (arg->running > 0) {
arg->running--;
}
if (arg->running == 0) {
if (arg->newobj_trace != 0) {
rb_tracepoint_disable(arg->newobj_trace);
}
if (arg->freeobj_trace != 0) {
rb_tracepoint_disable(arg->freeobj_trace);
}
}
return Qnil;
} Останавливает отслеживание выделения объектов.
Обратите внимание, что если ::trace_object_allocations_start вызывается n раз, то отслеживание остановится после n вызовов ::trace_object_allocations_stop.
static VALUE
undefine_final(VALUE os, VALUE obj)
{
return rb_undefine_finalizer(obj);
} Удаляет все финализаторы для obj.
Публичные методы экземпляров
# File ext/objspace/lib/objspace.rb, line 24
def dump(obj, output: :string)
out = case output
when :file, nil
require 'tempfile'
Tempfile.create(%w(rubyobj .json))
when :stdout
STDOUT
when :string
+''
when IO
output
else
raise ArgumentError, "wrong output option: #{output.inspect}"
end
ret = _dump(obj, out)
return nil if output == :stdout
ret
end Выводит содержимое объекта Ruby в формате JSON.
Этот метод ожидается работоспособным только с C Ruby. Это экспериментальный метод и может быть изменён. В частности, сигнатура функции и формат вывода не гарантируют совместимости в будущих версиях Ruby.
# File ext/objspace/lib/objspace.rb, line 72
def dump_all(output: :file, full: false, since: nil)
out = case output
when :file, nil
require 'tempfile'
Tempfile.create(%w(rubyheap .json))
when :stdout
STDOUT
when :string
+''
when IO
output
else
raise ArgumentError, "wrong output option: #{output.inspect}"
end
ret = _dump_all(out, full, since)
return nil if output == :stdout
ret
end Выводит содержимое кучи Ruby в формате JSON.
since должно быть неотрицательным целым числом или nil.
Если since — положительное целое число, то сохраняются только объекты этой и более поздних поколений. Текущее поколение можно получить с помощью GC::count.
Объекты, выделенные без включённого отслеживания выделения объектов, игнорируются. Подробнее см. ::trace_object_allocations.
Если since опущено или равно nil, сохраняются все объекты.
Этот метод ожидается работоспособным только с C Ruby. Это экспериментальный метод и может быть изменён. В частности, сигнатура функции и формат вывода не гарантируют совместимости в будущих версиях Ruby.
Приватные методы экземпляров
# File gc.rb, line 306 def garbage_collect full_mark: true, immediate_mark: true, immediate_sweep: true Primitive.gc_start_internal full_mark, immediate_mark, immediate_sweep, false end
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.