void ObjectMemory::snapshot(STATE) { // Assign all objects an object id... ObjectMemory::GCInhibit inhibitor(root_state_->om); // Walk the heap over and over until we don't create // any more objects... size_t last_seen = 0; while(last_object_id != last_seen) { last_seen = last_object_id; ObjectWalker walker(root_state_->om); GCData gc_data(state->vm()); // Seed it with the root objects. walker.seed(gc_data); Object* obj = walker.next(); while(obj) { obj->id(state); obj = walker.next(); } } // Now, save the current value of last_object_id, since thats // so we can compare later to find all new objects. last_snapshot_id = last_object_id; std::cout << "Snapshot taken: " << last_snapshot_id << "\n"; }
void ObjectMemory::find_referers(Object* target, ObjectArray& result) { ObjectMemory::GCInhibit inhibitor(this); ObjectWalker walker(root_state_->om); GCData gc_data(root_state_); // Seed it with the root objects. walker.seed(gc_data); Object* obj = walker.next(); RefererFinder rf(this, target); while(obj) { rf.reset(); rf.scan_object(obj); if(rf.found_p()) { result.push_back(obj); } obj = walker.next(); } }
Object* System::vm_deoptimize_all(STATE, Object* o_disable) { ObjectWalker walker(state->om); GCData gc_data(state); // Seed it with the root objects. walker.seed(gc_data); Object* obj = walker.next(); int total = 0; bool disable = RTEST(o_disable); while(obj) { if(CompiledMethod* cm = try_as<CompiledMethod>(obj)) { if(VMMethod* vmm = cm->backend_method()) { vmm->deoptimize(state, cm, disable); } total++; } obj = walker.next(); } return Integer::from(state, total); }
void VM::collect_maybe(CallFrame* call_frame) { this->set_call_frame(call_frame); // Stops all other threads, so we're only here by ourselves. StopTheWorld guard(this); GCData gc_data(this); if(om->collect_young_now) { if(shared.config.gc_show) { std::cout << get_current_time() << " [GC] Running young gen\n"; } om->collect_young_now = false; om->collect_young(gc_data); } if(om->collect_mature_now) { if(shared.config.gc_show) { std::cout << get_current_time() << " [GC] Running mature gen\n"; } om->collect_mature_now = false; om->collect_mature(gc_data); } }
void VM::collect(CallFrame* call_frame) { this->set_call_frame(call_frame); // Stops all other threads, so we're only here by ourselves. StopTheWorld guard(this); GCData gc_data(this); om->collect_young(gc_data); om->collect_mature(gc_data); }
void VM::collect(CallFrame* call_frame) { this->set_call_frame(call_frame); // Don't go any further unless we're allowed to GC. if(!om->can_gc()) return; // Stops all other threads, so we're only here by ourselves. StopTheWorld guard(this); GCData gc_data(this); om->collect_young(gc_data); om->collect_mature(gc_data); om->run_finalizers(this); }
void Env::find_all_compiled_code(compiled_code_iterator func, void* data) { ObjectWalker walker(private_->state()->memory()); GCData gc_data(private_->state()->vm()); walker.seed(gc_data); Env* env = private_->state()->vm()->tooling_env(); Object* obj = walker.next(); while(obj) { if(CompiledCode* code = try_as<CompiledCode>(obj)) { func(env, rbxti::o(code), data); } obj = walker.next(); } }
void ObjectMemory::print_new_since_snapshot(STATE) { // Assign all objects an object id... ObjectMemory::GCInhibit inhibitor(root_state_->om); ObjectWalker walker(root_state_->om); GCData gc_data(root_state_); // Seed it with the root objects. walker.seed(gc_data); Object* obj = walker.next(); // All reference ids are shifted up native_int check_id = (native_int)last_snapshot_id << 1; int count = 0; int bytes = 0; while(obj) { if(!obj->has_id(state) || obj->id(state)->to_native() > check_id) { count++; bytes += obj->size_in_bytes(state->vm()); if(kind_of<String>(obj)) { std::cout << "#<String:" << obj << ">\n"; } else { std::cout << obj->to_s(state, true)->c_str(state) << "\n"; } } obj = walker.next(); } std::cout << count << " objects since snapshot.\n"; std::cout << bytes << " bytes since snapshot.\n"; }
void VM::collect_maybe(CallFrame* call_frame) { this->set_call_frame(call_frame); // Don't go any further unless we're allowed to GC. if(!om->can_gc()) return; // Stops all other threads, so we're only here by ourselves. StopTheWorld guard(this); GCData gc_data(this); uint64_t start_time = 0; if(om->collect_young_now) { if(shared.config.gc_show) { start_time = get_current_time(); } YoungCollectStats stats; #ifdef RBX_PROFILER if(unlikely(shared.profiling())) { profiler::MethodEntry method(this, profiler::kYoungGC); om->collect_young(gc_data, &stats); } else { om->collect_young(gc_data, &stats); } #else om->collect_young(gc_data, &stats); #endif if(shared.config.gc_show) { uint64_t fin_time = get_current_time(); int diff = (fin_time - start_time) / 1000000; fprintf(stderr, "[GC %0.1f%% %d/%d %d %2dms]\n", stats.percentage_used, stats.promoted_objects, stats.excess_objects, stats.lifetime, diff); } } if(om->collect_mature_now) { int before_kb = 0; if(shared.config.gc_show) { start_time = get_current_time(); before_kb = om->mature_bytes_allocated() / 1024; } #ifdef RBX_PROFILER if(unlikely(shared.profiling())) { profiler::MethodEntry method(this, profiler::kMatureGC); om->collect_mature(gc_data); } else { om->collect_mature(gc_data); } #else om->collect_mature(gc_data); #endif if(shared.config.gc_show) { uint64_t fin_time = get_current_time(); int diff = (fin_time - start_time) / 1000000; int kb = om->mature_bytes_allocated() / 1024; fprintf(stderr, "[Full GC %dkB => %dkB %2dms]\n", before_kb, kb, diff); } } om->run_finalizers(this); }
void ObjectMemory::collect_maybe(STATE, GCToken gct, CallFrame* call_frame) { // Don't go any further unless we're allowed to GC. if(!can_gc()) return; while(!state->stop_the_world()) { state->checkpoint(gct, call_frame); // Someone else got to the GC before we did! No problem, all done! if(!collect_young_now && !collect_mature_now) return; } // Ok, everyone in stopped! LET'S GC! SYNC(state); state->shared().finalizer_handler()->start_collection(state); if(cDebugThreading) { std::cerr << std::endl << "[" << state << " WORLD beginning GC.]" << std::endl; } if(collect_young_now) { GCData gc_data(state->vm()); YoungCollectStats stats; RUBINIUS_GC_BEGIN(0); #ifdef RBX_PROFILER if(unlikely(state->vm()->tooling())) { tooling::GCEntry method(state, tooling::GCYoung); collect_young(state, &gc_data, &stats); } else { collect_young(state, &gc_data, &stats); } #else collect_young(state, &gc_data, &stats); #endif RUBINIUS_GC_END(0); print_young_stats(state, &gc_data, &stats); } if(collect_mature_now) { GCData* gc_data = new GCData(state->vm()); RUBINIUS_GC_BEGIN(1); #ifdef RBX_PROFILER if(unlikely(state->vm()->tooling())) { tooling::GCEntry method(state, tooling::GCMature); collect_mature(state, gc_data); } else { collect_mature(state, gc_data); } #else collect_mature(state, gc_data); #endif if(!mature_mark_concurrent_) { collect_mature_finish(state, gc_data); print_mature_stats(state, gc_data); } } state->restart_world(); UNSYNC; }
void ObjectMemory::collect_maybe(STATE, GCToken gct, CallFrame* call_frame) { // Don't go any further unless we're allowed to GC. if(!can_gc()) return; while(!state->stop_the_world()) { state->checkpoint(gct, call_frame); // Someone else got to the GC before we did! No problem, all done! if(!collect_young_now && !collect_mature_now) return; } // Ok, everyone in stopped! LET'S GC! SYNC(state); state->shared().finalizer_handler()->start_collection(state); if(cDebugThreading) { std::cerr << std::endl << "[" << state << " WORLD beginning GC.]" << std::endl; } GCData gc_data(state->vm(), gct); uint64_t start_time = 0; if(collect_young_now) { if(state->shared().config.gc_show) { start_time = get_current_time(); } YoungCollectStats stats; #ifdef RBX_PROFILER if(unlikely(state->vm()->tooling())) { tooling::GCEntry method(state, tooling::GCYoung); collect_young(gc_data, &stats); } else { collect_young(gc_data, &stats); } #else collect_young(gc_data, &stats); #endif if(state->shared().config.gc_show) { uint64_t fin_time = get_current_time(); int diff = (fin_time - start_time) / 1000000; std::cerr << "[GC " << std::fixed << std::setprecision(1) << stats.percentage_used << "% " << stats.promoted_objects << "/" << stats.excess_objects << " " << stats.lifetime << " " << diff << "ms]" << std::endl; if(state->shared().config.gc_noisy) { std::cerr << "\a" << std::flush; } } } if(collect_mature_now) { size_t before_kb = 0; if(state->shared().config.gc_show) { start_time = get_current_time(); before_kb = mature_bytes_allocated() / 1024; } #ifdef RBX_PROFILER if(unlikely(state->vm()->tooling())) { tooling::GCEntry method(state, tooling::GCMature); collect_mature(gc_data); } else { collect_mature(gc_data); } #else collect_mature(gc_data); #endif if(state->shared().config.gc_show) { uint64_t fin_time = get_current_time(); int diff = (fin_time - start_time) / 1000000; size_t kb = mature_bytes_allocated() / 1024; std::cerr << "[Full GC " << before_kb << "kB => " << kb << "kB " << diff << "ms]" << std::endl; if(state->shared().config.gc_noisy) { std::cerr << "\a\a" << std::flush; } } } state->shared().finalizer_handler()->finish_collection(state); state->restart_world(); UNSYNC; }
Object* System::vm_find_object(STATE, GCToken gct, Array* arg, Object* callable, CallFrame* calling_environment) { ObjectMemory::GCInhibit inhibitor(state->memory()); // Support an aux mode, where callable is an array and we just append // objects to it rather than #call it. Array* ary = try_as<Array>(callable); if(!ary) ary = nil<Array>(); Array* args = Array::create(state, 1); int total = 0; QueryCondition* condition = create_condition(state, arg); if(!condition) return Fixnum::from(0); Object* ret = cNil; // Special case for looking for an immediate if(Object* obj = condition->immediate()) { if(Symbol* sym = try_as<Symbol>(obj)) { // Check whether this is actually a valid symbol, not // some random non existing symbol. if(!state->shared().symbols.lookup_string(state, sym)) { delete condition; std::ostringstream msg; msg << "Invalid symbol 0x" << std::hex << reinterpret_cast<uintptr_t>(sym); Exception::range_error(state, msg.str().c_str()); return 0; } } if(!ary->nil_p()) { ary->append(state, obj); } else { args->set(state, 0, obj); ret = callable->send(state, calling_environment, G(sym_call), args, cNil, false); } delete condition; if(!ret) return 0; return Fixnum::from(1); } OnStack<2> os(state, ary, args); state->set_call_frame(calling_environment); ObjectWalker walker(state->memory()); GCData gc_data(state->vm()); { StopTheWorld stw(state, gct, calling_environment); // Seed it with the root objects. walker.seed(gc_data); } Object* obj = walker.next(); while(obj) { if(condition->perform(state, obj)) { total++; if(!ary->nil_p()) { ary->append(state, obj); } else { // We call back into Ruby land here, so that might trigger a GC // This ensures we mark all the locations of the current search // queue for the walker, so we update these object references // properly. Object** stack_buf = walker.stack_buf(); size_t stack_size = walker.stack_size(); Object** variable_buffer[stack_size]; for(size_t i = 0; i < stack_size; ++i) { variable_buffer[i] = &stack_buf[i]; } VariableRootBuffer vrb(state->vm()->current_root_buffers(), variable_buffer, stack_size); args->set(state, 0, obj); ret = callable->send(state, calling_environment, G(sym_call), args, cNil, false); if(!ret) break; } } obj = walker.next(); } delete condition; if(!ret) return 0; return Integer::from(state, total); }