diff options
Diffstat (limited to 'ext/objspace/objspace.c')
| -rw-r--r-- | ext/objspace/objspace.c | 590 |
1 files changed, 200 insertions, 390 deletions
diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c index 7afdfc1f6b..38bffb07f7 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -12,14 +12,13 @@ **********************************************************************/ -#include "gc.h" #include "internal.h" #include "internal/class.h" #include "internal/compilers.h" +#include "internal/gc.h" #include "internal/hash.h" #include "internal/imemo.h" #include "internal/sanitizers.h" -#include "node.h" #include "ruby/io.h" #include "ruby/re.h" #include "ruby/st.h" @@ -39,10 +38,11 @@ * information as only a *HINT*. Especially, the size of +T_DATA+ may not be * correct. * - * This method is only expected to work with C Ruby. + * This method is only expected to work with CRuby. * - * From Ruby 2.2, memsize_of(obj) returns a memory size includes - * sizeof(RVALUE). + * From Ruby 3.2 with Variable Width Allocation, it returns the actual slot + * size used plus any additional memory allocated outside the slot (such + * as external strings, arrays, or hash tables). */ static VALUE @@ -61,17 +61,10 @@ total_i(VALUE v, void *ptr) { struct total_data *data = (struct total_data *)ptr; - switch (BUILTIN_TYPE(v)) { - case T_NONE: - case T_IMEMO: - case T_ICLASS: - case T_NODE: - case T_ZOMBIE: - return; - default: - if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) { - data->total += rb_obj_memsize_of(v); - } + if (!rb_objspace_internal_object_p(v)) { + if (data->klass == 0 || rb_obj_is_kind_of(v, data->klass)) { + data->total += rb_obj_memsize_of(v); + } } } @@ -89,15 +82,15 @@ heap_iter(void *vstart, void *vend, size_t stride, void *ptr) VALUE v; for (v = (VALUE)vstart; v != (VALUE)vend; v += stride) { - void *poisoned = asan_poisoned_object_p(v); - asan_unpoison_object(v, false); + void *poisoned = rb_asan_poisoned_object_p(v); + rb_asan_unpoison_object(v, false); if (RBASIC(v)->flags) { (*ctx->cb)(v, ctx->data); } if (poisoned) { - asan_poison_object(v); + rb_asan_poison_object(v); } } @@ -115,28 +108,24 @@ each_object_with_flags(each_obj_with_flags cb, void *ctx) /* * call-seq: - * ObjectSpace.memsize_of_all([klass]) -> Integer + * ObjectSpace.memsize_of_all(klass = nil) -> integer * - * Return consuming memory size of all living objects in bytes. + * Returns the total memory size of all living objects in bytes. * - * If +klass+ (should be Class object) is given, return the total memory size - * of instances of the given class. + * ObjectSpace.memsize_of_all # => 12502001 * - * Note that the returned size is incomplete. You need to deal with this - * information as only a *HINT*. Especially, the size of +T_DATA+ may not be - * correct. + * If +klass+ is given (which must be a Class or Module), returns the total + * memory size of objects whose class is, or is a subclass, of +klass+. * - * Note that this method does *NOT* return total malloc'ed memory size. + * class MyClass; end + * ObjectSpace.memsize_of_all(MyClass) # => 0 + * o = MyClass.new + * ObjectSpace.memsize_of_all(MyClass) # => 40 * - * This method can be defined by the following Ruby code: - * - * 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 + * Note that the value returned may be an underestimate of the actual amount + * of memory used. Therefore, the value returned should only be used as a hint, + * rather than a source of truth. In particular, the size of +T_DATA+ objects may + * not be correct. * * This method is only expected to work with C Ruby. */ @@ -147,7 +136,8 @@ 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); + rb_scan_args(argc, argv, "01", &data.klass); + if (!NIL_P(data.klass)) rb_obj_is_kind_of(Qnil, data.klass); } each_object_with_flags(total_i, &data); @@ -177,8 +167,7 @@ setup_hash(int argc, VALUE *argv) hash = rb_hash_new(); } else if (!RHASH_EMPTY_P(hash)) { - /* WB: no new reference */ - st_foreach(RHASH_TBL_RAW(hash), set_zero_i, hash); + rb_hash_foreach(hash, set_zero_i, (st_data_t)hash); } return hash; @@ -197,33 +186,33 @@ type2sym(enum ruby_value_type i) VALUE type; switch (i) { #define CASE_TYPE(t) case t: type = ID2SYM(rb_intern(#t)); break; - CASE_TYPE(T_NONE); - CASE_TYPE(T_OBJECT); - CASE_TYPE(T_CLASS); - CASE_TYPE(T_MODULE); - CASE_TYPE(T_FLOAT); - CASE_TYPE(T_STRING); - CASE_TYPE(T_REGEXP); - CASE_TYPE(T_ARRAY); - CASE_TYPE(T_HASH); - CASE_TYPE(T_STRUCT); - CASE_TYPE(T_BIGNUM); - CASE_TYPE(T_FILE); - CASE_TYPE(T_DATA); - CASE_TYPE(T_MATCH); - CASE_TYPE(T_COMPLEX); - CASE_TYPE(T_RATIONAL); - CASE_TYPE(T_NIL); - CASE_TYPE(T_TRUE); - CASE_TYPE(T_FALSE); - CASE_TYPE(T_SYMBOL); - CASE_TYPE(T_FIXNUM); - CASE_TYPE(T_UNDEF); - CASE_TYPE(T_IMEMO); - CASE_TYPE(T_NODE); - CASE_TYPE(T_ICLASS); + CASE_TYPE(T_NONE); + CASE_TYPE(T_OBJECT); + CASE_TYPE(T_CLASS); + CASE_TYPE(T_MODULE); + CASE_TYPE(T_FLOAT); + CASE_TYPE(T_STRING); + CASE_TYPE(T_REGEXP); + CASE_TYPE(T_ARRAY); + CASE_TYPE(T_HASH); + CASE_TYPE(T_STRUCT); + CASE_TYPE(T_BIGNUM); + CASE_TYPE(T_FILE); + CASE_TYPE(T_DATA); + CASE_TYPE(T_MATCH); + CASE_TYPE(T_COMPLEX); + CASE_TYPE(T_RATIONAL); + CASE_TYPE(T_NIL); + CASE_TYPE(T_TRUE); + CASE_TYPE(T_FALSE); + CASE_TYPE(T_SYMBOL); + CASE_TYPE(T_FIXNUM); + CASE_TYPE(T_UNDEF); + CASE_TYPE(T_IMEMO); + CASE_TYPE(T_NODE); + CASE_TYPE(T_ICLASS); CASE_TYPE(T_MOVED); - CASE_TYPE(T_ZOMBIE); + CASE_TYPE(T_ZOMBIE); #undef CASE_TYPE default: rb_bug("type2sym: unknown type (%d)", i); } @@ -232,23 +221,26 @@ type2sym(enum ruby_value_type i) /* * call-seq: - * ObjectSpace.count_objects_size([result_hash]) -> hash + * ObjectSpace.count_objects_size(result_hash = {}) -> result_hash * * Counts objects size (in bytes) for each type. * - * Note that this information is incomplete. You need to deal with - * this information as only a *HINT*. Especially, total size of - * T_DATA may be wrong. + * Note that the returned size may not be accurate, so it should only + * be used as a hint. Specifically, the size for +T_DATA+ may be + * inaccurate because these are custom objects defined in Ruby and + * native extensions and so they may not accurately report their + * memory size. * - * It returns a hash as: - * {:TOTAL=>1461154, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249, ...} + * It returns a hash that looks like: * - * If the optional argument, result_hash, is given, - * it is overwritten and returned. - * This is intended to avoid probe effect. + * {TOTAL: 1461154, T_CLASS: 158280, T_MODULE: 20672, T_STRING: 527249, ...} * - * The contents of the returned hash is implementation defined. - * It may be changed in future. + * The contents of the returned hash are implementation specific and + * may be changed in future versions without notice. + * + * If the optional argument, +result_hash+, is given, + * it is overwritten and returned. + * This is intended to avoid the probe effect. * * This method is only expected to work with C Ruby. */ @@ -262,17 +254,17 @@ count_objects_size(int argc, VALUE *argv, VALUE os) VALUE hash = setup_hash(argc, argv); for (i = 0; i <= T_MASK; i++) { - counts[i] = 0; + 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])); - } + 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; @@ -303,28 +295,27 @@ size_t rb_sym_immortal_count(void); /* * call-seq: - * ObjectSpace.count_symbols([result_hash]) -> hash - * - * Counts symbols for each Symbol type. + * ObjectSpace.count_symbols(result_hash = nil) -> hash * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. + * Returns a hash containing the number of objects for each Symbol type. * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. + * The types of Symbols are the following: * - * Note: - * The contents of the returned hash is implementation defined. - * It may be changed in future. + * - +mortal_dynamic_symbol+: Symbols that are garbage collectable. + * - +immortal_dynamic_symbol+: Symbols that are objects allocated from the + * garbage collector, but are not garbage collectable. + * - +immortal_static_symbol+: Symbols that are not allocated from the + * garbage collector, and are thus not garbage collectable. + * - +immortal_symbol+: the sum of +immortal_dynamic_symbol+ and +immortal_static_symbol+. * - * This method is only expected to work with C Ruby. + * If the optional argument +result_hash+ is given, it is overwritten and + * returned. This is intended to avoid the probe effect. * - * On this version of MRI, they have 3 types of Symbols (and 1 total counts). + * This method is intended for developers interested in performance and memory + * usage of Ruby programs. The contents of the returned hash is implementation + * specific and may change in the future. * - * * 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) + * This method is only expected to work with C Ruby. */ static VALUE @@ -345,174 +336,6 @@ count_symbols(int argc, VALUE *argv, VALUE os) } static void -cn_i(VALUE v, void *n) -{ - size_t *nodes = (size_t *)n; - - if (BUILTIN_TYPE(v) == T_NODE) { - size_t s = nd_type((NODE *)v); - nodes[s]++; - } -} - -/* - * call-seq: - * ObjectSpace.count_nodes([result_hash]) -> hash - * - * Counts nodes for each node type. - * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. - * - * It returns a hash as: - * - * {:NODE_METHOD=>2027, :NODE_FBODY=>1927, :NODE_CFUNC=>1798, ...} - * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. - * - * Note: - * The contents of the returned hash is implementation defined. - * It may be changed in future. - * - * This method is only expected to work with 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_DASGN_CURR); - 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; -} - -static void cto_i(VALUE v, void *data) { VALUE hash = (VALUE)data; @@ -541,32 +364,22 @@ cto_i(VALUE v, void *data) /* * call-seq: - * ObjectSpace.count_tdata_objects([result_hash]) -> hash - * - * Counts objects for each +T_DATA+ type. - * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. - * - * It returns a hash as: - * - * {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. + * ObjectSpace.count_tdata_objects(result_hash = nil) -> hash * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. + * Returns a hash containing the number of objects for each +T_DATA+ type. + * The keys are Class objects when the +T_DATA+ object has an associated class, + * or Symbol objects of the name defined in the +rb_data_type_struct+ for internal + * +T_DATA+ objects. * - * The contents of the returned hash is implementation specific and may change - * in the future. + * ObjectSpace.count_tdata_objects + * # => {RBS::Location => 39255, marshal_compat_table: 1, Encoding => 103, mutex: 1, ... } * - * In this version, keys are Class object or Symbol object. + * If the optional argument +result_hash+ is given, it is overwritten and + * returned. This is intended to avoid the probe effect. * - * If object is kind of normal (accessible) object, the key is Class object. - * If object is not a kind of normal (internal) object, the key is symbol - * name, registered by rb_data_type_struct. + * This method is intended for developers interested in performance and memory + * usage of Ruby programs. The contents of the returned hash is implementation + * specific and may change in the future. * * This method is only expected to work with C Ruby. */ @@ -605,28 +418,22 @@ count_imemo_objects_i(VALUE v, void *data) /* * call-seq: - * ObjectSpace.count_imemo_objects([result_hash]) -> hash + * ObjectSpace.count_imemo_objects(result_hash = nil) -> hash * - * Counts objects for each +T_IMEMO+ type. + * Returns a hash containing the number of objects for each +T_IMEMO+ type. + * The keys are Symbol objects of the +T_IMEMO+ type name. + * +T_IMEMO+ objects are Ruby internal objects that are not visible to Ruby + * programs. * - * This method is only for MRI developers interested in performance and memory - * usage of Ruby programs. + * ObjectSpace.count_imemo_objects + * # => {imemo_callcache: 5482, imemo_constcache: 1258, imemo_ment: 13906, ... } * - * It returns a hash as: + * If the optional argument +result_hash+ is given, it is overwritten and + * returned. This is intended to avoid the probe effect. * - * {:imemo_ifunc=>8, - * :imemo_svar=>7, - * :imemo_cref=>509, - * :imemo_memo=>1, - * :imemo_throw_data=>1} - * - * If the optional argument, result_hash, is given, it is overwritten and - * returned. This is intended to avoid probe effect. - * - * The contents of the returned hash is implementation specific and may change - * in the future. - * - * In this version, keys are symbol objects. + * This method is intended for developers interested in performance and memory + * usage of Ruby programs. The contents of the returned hash is implementation + * specific and may change in the future. * * This method is only expected to work with C Ruby. */ @@ -637,20 +444,24 @@ 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"); +#define INIT_IMEMO_TYPE_ID(n) (imemo_type_ids[n] = rb_intern_const(#n)) + INIT_IMEMO_TYPE_ID(imemo_env); + INIT_IMEMO_TYPE_ID(imemo_cref); + INIT_IMEMO_TYPE_ID(imemo_svar); + INIT_IMEMO_TYPE_ID(imemo_throw_data); + INIT_IMEMO_TYPE_ID(imemo_ifunc); + INIT_IMEMO_TYPE_ID(imemo_memo); + INIT_IMEMO_TYPE_ID(imemo_ment); + INIT_IMEMO_TYPE_ID(imemo_iseq); + INIT_IMEMO_TYPE_ID(imemo_tmpbuf); + INIT_IMEMO_TYPE_ID(imemo_cvar_entry); + INIT_IMEMO_TYPE_ID(imemo_callinfo); + INIT_IMEMO_TYPE_ID(imemo_callcache); + INIT_IMEMO_TYPE_ID(imemo_constcache); + INIT_IMEMO_TYPE_ID(imemo_fields); + INIT_IMEMO_TYPE_ID(imemo_subclasses); + INIT_IMEMO_TYPE_ID(imemo_cdhash); +#undef INIT_IMEMO_TYPE_ID } each_object_with_flags(count_imemo_objects_i, (void *)hash); @@ -713,7 +524,7 @@ iow_internal_object_id(VALUE self) struct rof_data { VALUE refs; - VALUE internals; + VALUE values; }; static void @@ -723,12 +534,16 @@ reachable_object_from_i(VALUE obj, void *data_ptr) VALUE key = obj; VALUE val = obj; - if (rb_objspace_markable_object_p(obj)) { - if (rb_objspace_internal_object_p(obj)) { - val = iow_newobj(obj); - rb_ary_push(data->internals, val); - } - rb_hash_aset(data->refs, key, val); + if (!rb_objspace_garbage_object_p(obj)) { + if (NIL_P(rb_hash_lookup(data->refs, key))) { + rb_hash_aset(data->refs, key, Qtrue); + + if (rb_objspace_internal_object_p(obj)) { + val = iow_newobj(obj); + } + + rb_ary_push(data->values, val); + } } } @@ -744,63 +559,58 @@ collect_values(st_data_t key, st_data_t value, st_data_t data) * call-seq: * ObjectSpace.reachable_objects_from(obj) -> array or nil * - * [MRI specific feature] Return all reachable objects from `obj'. - * - * This method returns all reachable objects from `obj'. - * - * If `obj' has two or more references to the same object `x', then returned - * array only includes one `x' object. + * Returns all reachable objects from +obj+ as an array: * - * If `obj' is a non-markable (non-heap management) object such as true, - * false, nil, symbols and Fixnums (and Flonum) then it simply returns nil. + * ObjectSpace.reachable_objects_from(['a', 'b', 'c']) + * #=> [Array, 'a', 'b', 'c'] * - * If `obj' has references to an internal object, then it returns instances of - * ObjectSpace::InternalObjectWrapper class. This object contains a reference - * to an internal object and you can check the type of internal object with - * `type' method. + * The returned array is deduplicated, meaning that if +obj+ refers + * to another object more than once, it will only be added to the array + * once: * - * If `obj' is instance of ObjectSpace::InternalObjectWrapper class, then this - * method returns all reachable object from an internal object, which is - * pointed by `obj'. + * ObjectSpace.reachable_objects_from([v = 'a', v, v]) + * #=> [Array, 'a'] * - * With this method, you can find memory leaks. + * Returns +nil+ if +obj+ is not a markable object (i.e. non-heap + * managed) object. Non-markable objects include +true+, +false+, + * +nil+, certain symbols, small integers, and floats: * - * This method is only expected to work except with C Ruby. + * ObjectSpace.reachable_objects_from(1) + * #=> nil * - * Example: - * ObjectSpace.reachable_objects_from(['a', 'b', 'c']) - * #=> [Array, 'a', 'b', 'c'] + * All references to internal objects in the returned array are wrapped + * using ObjectSpace::InternalObjectWrapper objects. This object contains + * a reference to the internal object and the type of the object can + * be accessed using the ObjectSpace::InternalObjectWrapper#type method. * - * ObjectSpace.reachable_objects_from(['a', 'a', 'a']) - * #=> [Array, 'a', 'a', 'a'] # all 'a' strings have different object id + * If +obj+ is instance of ObjectSpace::InternalObjectWrapper, then this + * method returns all reachable object from the internal object. * - * ObjectSpace.reachable_objects_from([v = 'a', v, v]) - * #=> [Array, 'a'] - * - * ObjectSpace.reachable_objects_from(1) - * #=> nil # 1 is not markable (heap managed) object + * This method is useful for debugging purposes, such as finding + * memory leaks. * + * This method is only expected to work with C Ruby. */ static VALUE reachable_objects_from(VALUE self, VALUE obj) { - if (rb_objspace_markable_object_p(obj)) { - struct rof_data data; + if (!RB_SPECIAL_CONST_P(obj)) { + struct rof_data data; - if (rb_typeddata_is_kind_of(obj, &iow_data_type)) { - obj = (VALUE)DATA_PTR(obj); - } + 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(); + data.refs = rb_obj_hide(rb_ident_hash_new()); + data.values = rb_ary_new(); - rb_objspace_reachable_objects_from(obj, reachable_object_from_i, &data); + rb_objspace_reachable_objects_from(obj, reachable_object_from_i, &data); - return rb_funcall(data.refs, rb_intern("values"), 0); + return data.values; } else { - return Qnil; + return Qnil; } } @@ -819,26 +629,26 @@ reachable_object_from_root_i(const char *category, VALUE obj, void *ptr) VALUE category_objects; if (category == data->last_category) { - category_str = data->last_category_str; - category_objects = data->last_category_objects; + category_str = data->last_category_str; + category_objects = data->last_category_objects; } else { - data->last_category = category; - category_str = data->last_category_str = rb_str_new2(category); - category_objects = data->last_category_objects = rb_ident_hash_new(); - if (!NIL_P(rb_hash_lookup(data->categories, category_str))) { - rb_bug("reachable_object_from_root_i: category should insert at once"); - } - rb_hash_aset(data->categories, category_str, category_objects); + data->last_category = category; + category_str = data->last_category_str = rb_str_new2(category); + category_objects = data->last_category_objects = rb_ident_hash_new(); + if (!NIL_P(rb_hash_lookup(data->categories, category_str))) { + rb_bug("reachable_object_from_root_i: category should insert at once"); + } + rb_hash_aset(data->categories, category_str, category_objects); } - if (rb_objspace_markable_object_p(obj) && - obj != data->categories && - obj != data->last_category_objects) { - if (rb_objspace_internal_object_p(obj)) { - obj = iow_newobj(obj); - } - rb_hash_aset(category_objects, obj, obj); + if (!rb_objspace_garbage_object_p(obj) && + obj != data->categories && + obj != data->last_category_objects) { + if (rb_objspace_internal_object_p(obj)) { + obj = iow_newobj(obj); + } + rb_hash_aset(category_objects, obj, obj); } } @@ -874,14 +684,14 @@ static VALUE wrap_klass_iow(VALUE klass) { if (!RTEST(klass)) { - return Qnil; + return Qnil; } else if (RB_TYPE_P(klass, T_ICLASS) || CLASS_OF(klass) == Qfalse /* hidden object */) { - return iow_newobj(klass); + return iow_newobj(klass); } else { - return klass; + return klass; } } @@ -900,7 +710,7 @@ 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); + obj = (VALUE)DATA_PTR(obj); } if (RB_TYPE_P(obj, T_IMEMO)) { @@ -927,17 +737,17 @@ 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); + obj = (VALUE)DATA_PTR(obj); } switch (OBJ_BUILTIN_TYPE(obj)) { case T_MODULE: case T_CLASS: case T_ICLASS: - super = RCLASS_SUPER(obj); - break; + super = rb_class_super_of(obj); + break; default: - rb_raise(rb_eArgError, "class or module is expected"); + rb_raise(rb_eArgError, "class or module is expected"); } return wrap_klass_iow(super); @@ -955,7 +765,7 @@ void Init_objspace_dump(VALUE rb_mObjSpace); * * You need to <code>require 'objspace'</code> to use this extension module. * - * Generally, you *SHOULD NOT* use this library if you do not know + * Generally, you *SHOULD* *NOT* use this library if you do not know * about the MRI implementation. Mainly, this library is for (memory) * profiler developers and MRI developers who need to know about MRI * memory usage. @@ -976,7 +786,6 @@ Init_objspace(void) rb_define_module_function(rb_mObjSpace, "count_objects_size", count_objects_size, -1); rb_define_module_function(rb_mObjSpace, "count_symbols", count_symbols, -1); - rb_define_module_function(rb_mObjSpace, "count_nodes", count_nodes, -1); rb_define_module_function(rb_mObjSpace, "count_tdata_objects", count_tdata_objects, -1); rb_define_module_function(rb_mObjSpace, "count_imemo_objects", count_imemo_objects, -1); @@ -996,6 +805,7 @@ Init_objspace(void) * You can use the #type method to check the type of the internal object. */ rb_cInternalObjectWrapper = rb_define_class_under(rb_mObjSpace, "InternalObjectWrapper", rb_cObject); + rb_undef_alloc_func(rb_cInternalObjectWrapper); rb_define_method(rb_cInternalObjectWrapper, "type", iow_type, 0); rb_define_method(rb_cInternalObjectWrapper, "inspect", iow_inspect, 0); rb_define_method(rb_cInternalObjectWrapper, "internal_object_id", iow_internal_object_id, 0); |
