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 
RunInternal(double deadline_in_seconds)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 
ReachedIdleAllocationLimit(double scavenge_speed_in_bytes_per_ms,size_t new_space_size,size_t new_space_capacity)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 
EnoughIdleTimeForScavenge(double idle_time_in_ms,double scavenge_speed_in_bytes_per_ms,size_t new_space_size)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 
RescheduleIdleTask(Heap * heap)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 
ScheduleIdleTaskIfNeeded(Heap * heap,int bytes_allocated)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 
ScheduleIdleTask(Heap * heap)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