Index: runtime/vm/pages.cc |
=================================================================== |
--- runtime/vm/pages.cc (revision 29013) |
+++ runtime/vm/pages.cc (working copy) |
@@ -23,6 +23,12 @@ |
"Print free list statistics before a GC"); |
DEFINE_FLAG(bool, print_free_list_after_gc, false, |
"Print free list statistics after a GC"); |
+DEFINE_FLAG(bool, collect_code, true, |
+ "Attempt to GC infrequently used code."); |
+DEFINE_FLAG(int, code_collection_interval_in_us, 30000000, |
+ "Time between attempts to collect unused code."); |
+DEFINE_FLAG(bool, log_code_drop, false, |
+ "Emit a log message when pointers to unused code are dropped."); |
HeapPage* HeapPage::Initialize(VirtualMemory* memory, PageType type) { |
ASSERT(memory->size() > VirtualMemory::PageSize()); |
@@ -378,11 +384,68 @@ |
} |
+ |
+class CodeDetacherVisitor : public ObjectVisitor { |
+ public: |
+ explicit CodeDetacherVisitor(Isolate* isolate) : ObjectVisitor(isolate) { } |
+ |
+ virtual void VisitObject(RawObject* obj); |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(CodeDetacherVisitor); |
+}; |
+ |
+ |
+void CodeDetacherVisitor::VisitObject(RawObject* raw_obj) { |
+ Isolate* isolate = Isolate::Current(); |
+ HANDLESCOPE(isolate); |
+ const Object& obj = Object::Handle(raw_obj); |
+ if (obj.GetClassId() == kFunctionCid) { |
+ const Function& fn = Function::Cast(obj); |
+ if (!fn.HasOptimizedCode() && |
+ !fn.HasBreakpoint() && |
+ fn.HasCode() && // Not already detached. |
+ (fn.usage_counter() > 0)) { |
+ fn.set_usage_counter(fn.usage_counter() / 2); |
+ if (fn.usage_counter() == 0) { |
+ if (FLAG_log_code_drop) { |
+ const String& name = String::Handle(fn.name()); |
+ OS::Print("Detaching code for function %s\n", name.ToCString()); |
+ } |
+ fn.DetachCode(); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+void PageSpace::TryDetachingCode() { |
+ // Try to collect code if enough time has passed since the last attempt. |
+ const int64_t start = OS::GetCurrentTimeMicros(); |
+ const int64_t last_code_collection_in_us = |
+ page_space_controller_.last_code_collection_in_us(); |
+ if ((start - last_code_collection_in_us) > |
+ FLAG_code_collection_interval_in_us) { |
+ if (FLAG_log_code_drop) { |
+ OS::Print("Trying to detach code.\n"); |
+ } |
+ Isolate* isolate = Isolate::Current(); |
+ CodeDetacherVisitor code_detacher(isolate); |
+ heap_->IterateObjects(&code_detacher); |
+ page_space_controller_.set_last_code_collection_in_us(start); |
+ } |
+} |
+ |
+ |
void PageSpace::MarkSweep(bool invoke_api_callbacks) { |
// MarkSweep is not reentrant. Make sure that is the case. |
ASSERT(!sweeping_); |
sweeping_ = true; |
Isolate* isolate = Isolate::Current(); |
+ if (FLAG_collect_code) { |
+ TryDetachingCode(); |
+ } |
+ |
NoHandleScope no_handles(isolate); |
if (FLAG_print_free_list_before_gc) { |
@@ -398,7 +461,7 @@ |
OS::PrintErr(" done.\n"); |
} |
- int64_t start = OS::GetCurrentTimeMicros(); |
+ const int64_t start = OS::GetCurrentTimeMicros(); |
// Mark all reachable old-gen objects. |
GCMarker marker(heap_); |
@@ -490,7 +553,8 @@ |
heap_growth_ratio_(heap_growth_ratio), |
desired_utilization_((100.0 - heap_growth_ratio) / 100.0), |
heap_growth_rate_(heap_growth_rate), |
- garbage_collection_time_ratio_(garbage_collection_time_ratio) { |
+ garbage_collection_time_ratio_(garbage_collection_time_ratio), |
+ last_code_collection_in_us_(OS::GetCurrentTimeMicros()) { |
} |