1 // Copyright 2015 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/heap/scavenge-job.h" 6 7 #include "src/base/platform/time.h" 8 #include "src/heap/heap-inl.h" 9 #include "src/heap/heap.h" 10 #include "src/isolate.h" 11 #include "src/v8.h" 12 13 namespace v8 { 14 namespace internal { 15 16 17 const double ScavengeJob::kMaxAllocationLimitAsFractionOfNewSpace = 0.8; 18 19 void ScavengeJob::IdleTask::RunInternal(double deadline_in_seconds) { 20 Heap* heap = isolate()->heap(); 21 double deadline_in_ms = 22 deadline_in_seconds * 23 static_cast<double>(base::Time::kMillisecondsPerSecond); 24 double start_ms = heap->MonotonicallyIncreasingTimeInMs(); 25 double idle_time_in_ms = deadline_in_ms - start_ms; 26 double scavenge_speed_in_bytes_per_ms = 27 heap->tracer()->ScavengeSpeedInBytesPerMillisecond(); 28 size_t new_space_size = heap->new_space()->Size(); 29 size_t new_space_capacity = heap->new_space()->Capacity(); 30 31 job_->NotifyIdleTask(); 32 33 if (ReachedIdleAllocationLimit(scavenge_speed_in_bytes_per_ms, new_space_size, 34 new_space_capacity)) { 35 if (EnoughIdleTimeForScavenge( 36 idle_time_in_ms, scavenge_speed_in_bytes_per_ms, new_space_size)) { 37 heap->CollectGarbage(NEW_SPACE, GarbageCollectionReason::kIdleTask); 38 } else { 39 // Immediately request another idle task that can get larger idle time. 40 job_->RescheduleIdleTask(heap); 41 } 42 } 43 } 44 45 bool ScavengeJob::ReachedIdleAllocationLimit( 46 double scavenge_speed_in_bytes_per_ms, size_t new_space_size, 47 size_t new_space_capacity) { 48 if (scavenge_speed_in_bytes_per_ms == 0) { 49 scavenge_speed_in_bytes_per_ms = kInitialScavengeSpeedInBytesPerMs; 50 } 51 52 // Set the allocation limit to the number of bytes we can scavenge in an 53 // average idle task. 54 double allocation_limit = kAverageIdleTimeMs * scavenge_speed_in_bytes_per_ms; 55 56 // Keep the limit smaller than the new space capacity. 57 allocation_limit = 58 Min<double>(allocation_limit, 59 new_space_capacity * kMaxAllocationLimitAsFractionOfNewSpace); 60 // Adjust the limit to take into account bytes that will be allocated until 61 // the next check and keep the limit large enough to avoid scavenges in tiny 62 // new space. 63 allocation_limit = 64 Max<double>(allocation_limit - kBytesAllocatedBeforeNextIdleTask, 65 kMinAllocationLimit); 66 67 return allocation_limit <= new_space_size; 68 } 69 70 bool ScavengeJob::EnoughIdleTimeForScavenge( 71 double idle_time_in_ms, double scavenge_speed_in_bytes_per_ms, 72 size_t new_space_size) { 73 if (scavenge_speed_in_bytes_per_ms == 0) { 74 scavenge_speed_in_bytes_per_ms = kInitialScavengeSpeedInBytesPerMs; 75 } 76 return new_space_size <= idle_time_in_ms * scavenge_speed_in_bytes_per_ms; 77 } 78 79 80 void ScavengeJob::RescheduleIdleTask(Heap* heap) { 81 // Make sure that we don't reschedule more than one time. 82 // Otherwise, we might spam the scheduler with idle tasks. 83 if (!idle_task_rescheduled_) { 84 ScheduleIdleTask(heap); 85 idle_task_rescheduled_ = true; 86 } 87 } 88 89 90 void ScavengeJob::ScheduleIdleTaskIfNeeded(Heap* heap, int bytes_allocated) { 91 bytes_allocated_since_the_last_task_ += bytes_allocated; 92 if (bytes_allocated_since_the_last_task_ >= 93 static_cast<int>(kBytesAllocatedBeforeNextIdleTask)) { 94 ScheduleIdleTask(heap); 95 bytes_allocated_since_the_last_task_ = 0; 96 idle_task_rescheduled_ = false; 97 } 98 } 99 100 101 void ScavengeJob::ScheduleIdleTask(Heap* heap) { 102 if (!idle_task_pending_) { 103 v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(heap->isolate()); 104 if (V8::GetCurrentPlatform()->IdleTasksEnabled(isolate)) { 105 idle_task_pending_ = true; 106 auto task = new IdleTask(heap->isolate(), this); 107 V8::GetCurrentPlatform()->CallIdleOnForegroundThread(isolate, task); 108 } 109 } 110 } 111 } // namespace internal 112 } // namespace v8 113