Skip to content

Commit fb26d0b

Browse files
Dominik InführCommit Bot
authored andcommitted
[objects] Compact and shrink script_list
So far creating scripts always grew the script_list without ever reusing cleared slots or shrinking. While this is probably not a problem with script_list in practice, this is still a memory leak. Fix this leak by using WeakArrayList::Append instead of AddToEnd. Append adds to the end of the array, but potentially compacts and shrinks the list as well. Other WeakArrayLists can use this method as well, as long as they are not using indices into this array. Bug: v8:10031 Change-Id: If743c4cc3f8d67ab735522f0ded038b2fb43e437 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1967385 Commit-Queue: Dominik Inführ <dinfuehr@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#65640}
1 parent 6c83637 commit fb26d0b

6 files changed

Lines changed: 214 additions & 15 deletions

File tree

src/heap/factory.cc

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,8 +1644,8 @@ Handle<Script> Factory::NewScriptWithId(Handle<String> source, int script_id) {
16441644
script->set_flags(0);
16451645
script->set_host_defined_options(*empty_fixed_array());
16461646
Handle<WeakArrayList> scripts = script_list();
1647-
scripts = WeakArrayList::AddToEnd(isolate(), scripts,
1648-
MaybeObjectHandle::Weak(script));
1647+
scripts = WeakArrayList::Append(isolate(), scripts,
1648+
MaybeObjectHandle::Weak(script));
16491649
heap->set_script_list(*scripts);
16501650
LOG(isolate(), ScriptEvent(Logger::ScriptEventType::kCreate, script_id));
16511651
return script;
@@ -2075,6 +2075,29 @@ Handle<FixedArray> Factory::CopyFixedArrayAndGrow(Handle<FixedArray> array,
20752075
return CopyArrayAndGrow(array, grow_by, AllocationType::kYoung);
20762076
}
20772077

2078+
Handle<WeakArrayList> Factory::NewUninitializedWeakArrayList(
2079+
int capacity, AllocationType allocation) {
2080+
DCHECK_LE(0, capacity);
2081+
if (capacity == 0) return empty_weak_array_list();
2082+
2083+
HeapObject obj = AllocateRawWeakArrayList(capacity, allocation);
2084+
obj.set_map_after_allocation(*weak_array_list_map(), SKIP_WRITE_BARRIER);
2085+
2086+
Handle<WeakArrayList> result(WeakArrayList::cast(obj), isolate());
2087+
result->set_length(0);
2088+
result->set_capacity(capacity);
2089+
return result;
2090+
}
2091+
2092+
Handle<WeakArrayList> Factory::NewWeakArrayList(int capacity,
2093+
AllocationType allocation) {
2094+
Handle<WeakArrayList> result =
2095+
NewUninitializedWeakArrayList(capacity, allocation);
2096+
MemsetTagged(ObjectSlot(result->data_start()),
2097+
ReadOnlyRoots(isolate()).undefined_value(), capacity);
2098+
return result;
2099+
}
2100+
20782101
Handle<WeakFixedArray> Factory::CopyWeakFixedArrayAndGrow(
20792102
Handle<WeakFixedArray> src, int grow_by) {
20802103
DCHECK(!src->IsTransitionArray()); // Compacted by GC, this code doesn't work
@@ -2086,22 +2109,42 @@ Handle<WeakArrayList> Factory::CopyWeakArrayListAndGrow(
20862109
int old_capacity = src->capacity();
20872110
int new_capacity = old_capacity + grow_by;
20882111
DCHECK_GE(new_capacity, old_capacity);
2089-
HeapObject obj = AllocateRawWeakArrayList(new_capacity, allocation);
2090-
obj.set_map_after_allocation(src->map(), SKIP_WRITE_BARRIER);
2091-
2092-
WeakArrayList result = WeakArrayList::cast(obj);
2112+
Handle<WeakArrayList> result =
2113+
NewUninitializedWeakArrayList(new_capacity, allocation);
20932114
int old_len = src->length();
2094-
result.set_length(old_len);
2095-
result.set_capacity(new_capacity);
2115+
result->set_length(old_len);
20962116

20972117
// Copy the content.
20982118
DisallowHeapAllocation no_gc;
2099-
WriteBarrierMode mode = obj.GetWriteBarrierMode(no_gc);
2100-
result.CopyElements(isolate(), 0, *src, 0, old_len, mode);
2101-
MemsetTagged(ObjectSlot(result.data_start() + old_len),
2119+
WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
2120+
result->CopyElements(isolate(), 0, *src, 0, old_len, mode);
2121+
MemsetTagged(ObjectSlot(result->data_start() + old_len),
21022122
ReadOnlyRoots(isolate()).undefined_value(),
21032123
new_capacity - old_len);
2104-
return Handle<WeakArrayList>(result, isolate());
2124+
return result;
2125+
}
2126+
2127+
Handle<WeakArrayList> Factory::CompactWeakArrayList(Handle<WeakArrayList> src,
2128+
int new_capacity,
2129+
AllocationType allocation) {
2130+
Handle<WeakArrayList> result =
2131+
NewUninitializedWeakArrayList(new_capacity, allocation);
2132+
2133+
// Copy the content.
2134+
DisallowHeapAllocation no_gc;
2135+
WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc);
2136+
int copy_to = 0, length = src->length();
2137+
for (int i = 0; i < length; i++) {
2138+
MaybeObject element = src->Get(i);
2139+
if (element->IsCleared()) continue;
2140+
result->Set(copy_to++, element, mode);
2141+
}
2142+
result->set_length(copy_to);
2143+
2144+
MemsetTagged(ObjectSlot(result->data_start() + copy_to),
2145+
ReadOnlyRoots(isolate()).undefined_value(),
2146+
new_capacity - copy_to);
2147+
return result;
21052148
}
21062149

21072150
Handle<PropertyArray> Factory::CopyPropertyArrayAndGrow(

src/heap/factory.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,13 +514,20 @@ class V8_EXPORT_PRIVATE Factory {
514514
Handle<FixedArray> CopyFixedArrayAndGrow(Handle<FixedArray> array,
515515
int grow_by);
516516

517+
Handle<WeakArrayList> NewWeakArrayList(
518+
int capacity, AllocationType allocation = AllocationType::kYoung);
519+
517520
Handle<WeakFixedArray> CopyWeakFixedArrayAndGrow(Handle<WeakFixedArray> array,
518521
int grow_by);
519522

520523
Handle<WeakArrayList> CopyWeakArrayListAndGrow(
521524
Handle<WeakArrayList> array, int grow_by,
522525
AllocationType allocation = AllocationType::kYoung);
523526

527+
Handle<WeakArrayList> CompactWeakArrayList(
528+
Handle<WeakArrayList> array, int new_capacity,
529+
AllocationType allocation = AllocationType::kYoung);
530+
524531
Handle<PropertyArray> CopyPropertyArrayAndGrow(Handle<PropertyArray> array,
525532
int grow_by);
526533

@@ -1093,6 +1100,10 @@ class V8_EXPORT_PRIVATE Factory {
10931100
// Initializes JSObject body starting at given offset.
10941101
void InitializeJSObjectBody(Handle<JSObject> obj, Handle<Map> map,
10951102
int start_offset);
1103+
1104+
private:
1105+
Handle<WeakArrayList> NewUninitializedWeakArrayList(
1106+
int capacity, AllocationType allocation = AllocationType::kYoung);
10961107
};
10971108

10981109
// Utility class to simplify argument handling around JSFunction creation.

src/objects/fixed-array.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,17 @@ class WeakArrayList : public HeapObject {
344344
Isolate* isolate, Handle<WeakArrayList> array,
345345
const MaybeObjectHandle& value1, const MaybeObjectHandle& value2);
346346

347+
// Appends an element to the array and possibly compacts and shrinks live weak
348+
// references to the start of the collection. Only use this method when
349+
// indices to elements can change.
350+
static Handle<WeakArrayList> Append(
351+
Isolate* isolate, Handle<WeakArrayList> array,
352+
const MaybeObjectHandle& value,
353+
AllocationType allocation = AllocationType::kYoung);
354+
355+
// Compact weak references to the beginning of the array.
356+
V8_EXPORT_PRIVATE void Compact(Isolate* isolate);
357+
347358
inline MaybeObject Get(int index) const;
348359
inline MaybeObject Get(const Isolate* isolate, int index) const;
349360

@@ -357,6 +368,10 @@ class WeakArrayList : public HeapObject {
357368
return kHeaderSize + capacity * kTaggedSize;
358369
}
359370

371+
static constexpr int CapacityForLength(int length) {
372+
return length + Max(length / 2, 2);
373+
}
374+
360375
// Gives access to raw memory which stores the array's data.
361376
inline MaybeObjectSlot data_start();
362377

@@ -387,6 +402,9 @@ class WeakArrayList : public HeapObject {
387402
// Returns the number of non-cleaned weak references in the array.
388403
int CountLiveWeakReferences() const;
389404

405+
// Returns the number of non-cleaned elements in the array.
406+
int CountLiveElements() const;
407+
390408
// Returns whether an entry was found and removed. Will move the elements
391409
// around in the array - this method can only be used in cases where the user
392410
// doesn't care about the indices! Users should make sure there are no

src/objects/objects.cc

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3971,6 +3971,65 @@ Handle<WeakArrayList> WeakArrayList::AddToEnd(Isolate* isolate,
39713971
return array;
39723972
}
39733973

3974+
// static
3975+
Handle<WeakArrayList> WeakArrayList::Append(Isolate* isolate,
3976+
Handle<WeakArrayList> array,
3977+
const MaybeObjectHandle& value,
3978+
AllocationType allocation) {
3979+
int length = array->length();
3980+
3981+
if (length < array->capacity()) {
3982+
array->Set(length, *value);
3983+
array->set_length(length + 1);
3984+
return array;
3985+
}
3986+
3987+
// Not enough space in the array left, either grow, shrink or
3988+
// compact the array.
3989+
int new_length = array->CountLiveElements() + 1;
3990+
3991+
bool shrink = new_length < length / 4;
3992+
bool grow = 3 * (length / 4) < new_length;
3993+
3994+
if (shrink || grow) {
3995+
// Grow or shrink array and compact out-of-place.
3996+
int new_capacity = CapacityForLength(new_length);
3997+
array = isolate->factory()->CompactWeakArrayList(array, new_capacity,
3998+
allocation);
3999+
4000+
} else {
4001+
// Perform compaction in the current array.
4002+
array->Compact(isolate);
4003+
}
4004+
4005+
// Now append value to the array, there should always be enough space now.
4006+
DCHECK_LT(array->length(), array->capacity());
4007+
4008+
// Reload length, allocation might have killed some weak refs.
4009+
int index = array->length();
4010+
array->Set(index, *value);
4011+
array->set_length(index + 1);
4012+
return array;
4013+
}
4014+
4015+
void WeakArrayList::Compact(Isolate* isolate) {
4016+
int length = this->length();
4017+
int new_length = 0;
4018+
4019+
for (int i = 0; i < length; i++) {
4020+
MaybeObject value = Get(isolate, i);
4021+
4022+
if (!value->IsCleared()) {
4023+
if (new_length != i) {
4024+
Set(new_length, value);
4025+
}
4026+
++new_length;
4027+
}
4028+
}
4029+
4030+
set_length(new_length);
4031+
}
4032+
39744033
bool WeakArrayList::IsFull() { return length() == capacity(); }
39754034

39764035
// static
@@ -3980,9 +4039,7 @@ Handle<WeakArrayList> WeakArrayList::EnsureSpace(Isolate* isolate,
39804039
AllocationType allocation) {
39814040
int capacity = array->capacity();
39824041
if (capacity < length) {
3983-
int new_capacity = length;
3984-
new_capacity = new_capacity + Max(new_capacity / 2, 2);
3985-
int grow_by = new_capacity - capacity;
4042+
int grow_by = CapacityForLength(length) - capacity;
39864043
array = isolate->factory()->CopyWeakArrayListAndGrow(array, grow_by,
39874044
allocation);
39884045
}
@@ -3999,6 +4056,16 @@ int WeakArrayList::CountLiveWeakReferences() const {
39994056
return live_weak_references;
40004057
}
40014058

4059+
int WeakArrayList::CountLiveElements() const {
4060+
int non_cleared_objects = 0;
4061+
for (int i = 0; i < length(); i++) {
4062+
if (!Get(i)->IsCleared()) {
4063+
++non_cleared_objects;
4064+
}
4065+
}
4066+
return non_cleared_objects;
4067+
}
4068+
40024069
bool WeakArrayList::RemoveOne(const MaybeObjectHandle& value) {
40034070
if (length() == 0) return false;
40044071
// Optimize for the most recently added element to be removed again.

test/unittests/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ v8_source_set("unittests_sources") {
209209
"objects/object-unittest.cc",
210210
"objects/osr-optimized-code-cache-unittest.cc",
211211
"objects/value-serializer-unittest.cc",
212+
"objects/weakarraylist-unittest.cc",
212213
"parser/ast-value-unittest.cc",
213214
"parser/preparser-unittest.cc",
214215
"profiler/strings-storage-unittest.cc",
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2019 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/factory.h"
6+
#include "test/unittests/test-utils.h"
7+
8+
namespace v8 {
9+
namespace internal {
10+
11+
using WeakArrayListTest = TestWithIsolate;
12+
13+
TEST_F(WeakArrayListTest, Compact) {
14+
Handle<WeakArrayList> list = isolate()->factory()->NewWeakArrayList(10);
15+
EXPECT_EQ(list->length(), 0);
16+
EXPECT_EQ(list->capacity(), 10);
17+
18+
MaybeObject some_object =
19+
MaybeObject::FromObject(*isolate()->factory()->empty_fixed_array());
20+
MaybeObject weak_ref = MaybeObject::MakeWeak(some_object);
21+
MaybeObject smi = MaybeObject::FromSmi(Smi::FromInt(0));
22+
MaybeObject cleared_ref = HeapObjectReference::ClearedValue(isolate());
23+
list->Set(0, weak_ref);
24+
list->Set(1, smi);
25+
list->Set(2, cleared_ref);
26+
list->Set(3, cleared_ref);
27+
list->set_length(5);
28+
29+
list->Compact(isolate());
30+
EXPECT_EQ(list->length(), 3);
31+
EXPECT_EQ(list->capacity(), 10);
32+
}
33+
34+
TEST_F(WeakArrayListTest, OutOfPlaceCompact) {
35+
Handle<WeakArrayList> list = isolate()->factory()->NewWeakArrayList(20);
36+
EXPECT_EQ(list->length(), 0);
37+
EXPECT_EQ(list->capacity(), 20);
38+
39+
MaybeObject some_object =
40+
MaybeObject::FromObject(*isolate()->factory()->empty_fixed_array());
41+
MaybeObject weak_ref = MaybeObject::MakeWeak(some_object);
42+
MaybeObject smi = MaybeObject::FromSmi(Smi::FromInt(0));
43+
MaybeObject cleared_ref = HeapObjectReference::ClearedValue(isolate());
44+
list->Set(0, weak_ref);
45+
list->Set(1, smi);
46+
list->Set(2, cleared_ref);
47+
list->Set(3, smi);
48+
list->Set(4, cleared_ref);
49+
list->set_length(6);
50+
51+
Handle<WeakArrayList> compacted =
52+
isolate()->factory()->CompactWeakArrayList(list, 4);
53+
EXPECT_EQ(list->length(), 6);
54+
EXPECT_EQ(compacted->length(), 4);
55+
EXPECT_EQ(compacted->capacity(), 4);
56+
}
57+
58+
} // namespace internal
59+
} // namespace v8

0 commit comments

Comments
 (0)