OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/heap/mark-compact.h" | 5 #include "src/heap/mark-compact.h" |
6 | 6 |
7 #include "src/base/atomicops.h" | 7 #include "src/base/atomicops.h" |
8 #include "src/base/bits.h" | 8 #include "src/base/bits.h" |
9 #include "src/base/sys-info.h" | 9 #include "src/base/sys-info.h" |
10 #include "src/code-stubs.h" | 10 #include "src/code-stubs.h" |
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 | 342 |
343 void MarkCompactCollector::CollectGarbage() { | 343 void MarkCompactCollector::CollectGarbage() { |
344 // Make sure that Prepare() has been called. The individual steps below will | 344 // Make sure that Prepare() has been called. The individual steps below will |
345 // update the state as they proceed. | 345 // update the state as they proceed. |
346 DCHECK(state_ == PREPARE_GC); | 346 DCHECK(state_ == PREPARE_GC); |
347 | 347 |
348 MarkLiveObjects(); | 348 MarkLiveObjects(); |
349 | 349 |
350 DCHECK(heap_->incremental_marking()->IsStopped()); | 350 DCHECK(heap_->incremental_marking()->IsStopped()); |
351 | 351 |
352 // ClearNonLiveReferences can deoptimize code in dependent code arrays. | 352 ProcessWeakReferences(); |
353 // Process weak cells before so that weak cells in dependent code | |
354 // arrays are cleared or contain only live code objects. | |
355 ProcessAndClearWeakCells(); | |
356 | |
357 ClearNonLiveReferences(); | |
358 | |
359 ClearWeakCollections(); | |
360 | |
361 heap_->set_encountered_weak_cells(Smi::FromInt(0)); | |
362 | 353 |
363 #ifdef VERIFY_HEAP | 354 #ifdef VERIFY_HEAP |
364 if (FLAG_verify_heap) { | 355 if (FLAG_verify_heap) { |
365 VerifyMarking(heap_); | 356 VerifyMarking(heap_); |
366 } | 357 } |
367 #endif | 358 #endif |
368 | 359 |
369 ClearInvalidStoreAndSlotsBufferEntries(); | 360 ClearInvalidStoreAndSlotsBufferEntries(); |
370 | 361 |
371 #ifdef VERIFY_HEAP | 362 #ifdef VERIFY_HEAP |
(...skipping 1442 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1814 if (!code->CanDeoptAt(it.frame()->pc())) { | 1805 if (!code->CanDeoptAt(it.frame()->pc())) { |
1815 Code::BodyDescriptor::IterateBody(code, visitor); | 1806 Code::BodyDescriptor::IterateBody(code, visitor); |
1816 } | 1807 } |
1817 ProcessMarkingDeque(); | 1808 ProcessMarkingDeque(); |
1818 return; | 1809 return; |
1819 } | 1810 } |
1820 } | 1811 } |
1821 } | 1812 } |
1822 | 1813 |
1823 | 1814 |
| 1815 bool ShouldRetainMap(Map* map, int age) { |
| 1816 if (age == 0) { |
| 1817 // The map has aged. Do not retain this map. |
| 1818 return false; |
| 1819 } |
| 1820 Object* constructor = map->GetConstructor(); |
| 1821 if (!constructor->IsHeapObject() || |
| 1822 Marking::IsWhite(Marking::MarkBitFrom(HeapObject::cast(constructor)))) { |
| 1823 // The constructor is dead, no new objects with this map can |
| 1824 // be created. Do not retain this map. |
| 1825 return false; |
| 1826 } |
| 1827 return true; |
| 1828 } |
| 1829 |
| 1830 |
1824 void MarkCompactCollector::RetainMaps() { | 1831 void MarkCompactCollector::RetainMaps() { |
1825 if (heap()->ShouldReduceMemory() || heap()->ShouldAbortIncrementalMarking() || | 1832 // Do not retain dead maps if flag disables it or there is |
1826 FLAG_retain_maps_for_n_gc == 0) { | 1833 // - memory pressure (reduce_memory_footprint_), |
1827 // Do not retain dead maps if flag disables it or there is | 1834 // - GC is requested by tests or dev-tools (abort_incremental_marking_). |
1828 // - memory pressure (reduce_memory_footprint_), | 1835 bool map_retaining_is_disabled = heap()->ShouldReduceMemory() || |
1829 // - GC is requested by tests or dev-tools (abort_incremental_marking_). | 1836 heap()->ShouldAbortIncrementalMarking() || |
1830 return; | 1837 FLAG_retain_maps_for_n_gc == 0; |
1831 } | |
1832 | 1838 |
1833 ArrayList* retained_maps = heap()->retained_maps(); | 1839 ArrayList* retained_maps = heap()->retained_maps(); |
1834 int length = retained_maps->Length(); | 1840 int length = retained_maps->Length(); |
1835 int new_length = 0; | 1841 int new_length = 0; |
| 1842 // The number_of_disposed_maps separates maps in the retained_maps |
| 1843 // array that were created before and after context disposal. |
| 1844 // We do not age and retain disposed maps to avoid memory leaks. |
| 1845 int number_of_disposed_maps = heap()->number_of_disposed_maps_; |
| 1846 int new_number_of_disposed_maps = 0; |
| 1847 // This loop compacts the array by removing cleared weak cells, |
| 1848 // ages and retains dead maps. |
1836 for (int i = 0; i < length; i += 2) { | 1849 for (int i = 0; i < length; i += 2) { |
1837 DCHECK(retained_maps->Get(i)->IsWeakCell()); | 1850 DCHECK(retained_maps->Get(i)->IsWeakCell()); |
1838 WeakCell* cell = WeakCell::cast(retained_maps->Get(i)); | 1851 WeakCell* cell = WeakCell::cast(retained_maps->Get(i)); |
1839 if (cell->cleared()) continue; | 1852 if (cell->cleared()) continue; |
1840 int age = Smi::cast(retained_maps->Get(i + 1))->value(); | 1853 int age = Smi::cast(retained_maps->Get(i + 1))->value(); |
1841 int new_age; | 1854 int new_age; |
1842 Map* map = Map::cast(cell->value()); | 1855 Map* map = Map::cast(cell->value()); |
1843 MarkBit map_mark = Marking::MarkBitFrom(map); | 1856 MarkBit map_mark = Marking::MarkBitFrom(map); |
1844 if (Marking::IsWhite(map_mark)) { | 1857 if (i >= number_of_disposed_maps && !map_retaining_is_disabled && |
1845 if (age == 0) { | 1858 Marking::IsWhite(map_mark)) { |
1846 // The map has aged. Do not retain this map. | 1859 if (ShouldRetainMap(map, age)) { |
1847 continue; | 1860 MarkObject(map, map_mark); |
1848 } | |
1849 Object* constructor = map->GetConstructor(); | |
1850 if (!constructor->IsHeapObject() || Marking::IsWhite(Marking::MarkBitFrom( | |
1851 HeapObject::cast(constructor)))) { | |
1852 // The constructor is dead, no new objects with this map can | |
1853 // be created. Do not retain this map. | |
1854 continue; | |
1855 } | 1861 } |
1856 Object* prototype = map->prototype(); | 1862 Object* prototype = map->prototype(); |
1857 if (prototype->IsHeapObject() && | 1863 if (age > 0 && prototype->IsHeapObject() && |
1858 Marking::IsWhite(Marking::MarkBitFrom(HeapObject::cast(prototype)))) { | 1864 Marking::IsWhite(Marking::MarkBitFrom(HeapObject::cast(prototype)))) { |
1859 // The prototype is not marked, age the map. | 1865 // The prototype is not marked, age the map. |
1860 new_age = age - 1; | 1866 new_age = age - 1; |
1861 } else { | 1867 } else { |
1862 // The prototype and the constructor are marked, this map keeps only | 1868 // The prototype and the constructor are marked, this map keeps only |
1863 // transition tree alive, not JSObjects. Do not age the map. | 1869 // transition tree alive, not JSObjects. Do not age the map. |
1864 new_age = age; | 1870 new_age = age; |
1865 } | 1871 } |
1866 MarkObject(map, map_mark); | |
1867 } else { | 1872 } else { |
1868 new_age = FLAG_retain_maps_for_n_gc; | 1873 new_age = FLAG_retain_maps_for_n_gc; |
1869 } | 1874 } |
| 1875 // Compact the array and update the age. |
1870 if (i != new_length) { | 1876 if (i != new_length) { |
1871 retained_maps->Set(new_length, cell); | 1877 retained_maps->Set(new_length, cell); |
1872 Object** slot = retained_maps->Slot(new_length); | 1878 Object** slot = retained_maps->Slot(new_length); |
1873 RecordSlot(retained_maps, slot, cell); | 1879 RecordSlot(retained_maps, slot, cell); |
1874 retained_maps->Set(new_length + 1, Smi::FromInt(new_age)); | 1880 retained_maps->Set(new_length + 1, Smi::FromInt(new_age)); |
1875 } else if (new_age != age) { | 1881 } else if (new_age != age) { |
1876 retained_maps->Set(new_length + 1, Smi::FromInt(new_age)); | 1882 retained_maps->Set(new_length + 1, Smi::FromInt(new_age)); |
1877 } | 1883 } |
| 1884 if (i < number_of_disposed_maps) { |
| 1885 new_number_of_disposed_maps++; |
| 1886 } |
1878 new_length += 2; | 1887 new_length += 2; |
1879 } | 1888 } |
| 1889 heap()->number_of_disposed_maps_ = new_number_of_disposed_maps; |
1880 Object* undefined = heap()->undefined_value(); | 1890 Object* undefined = heap()->undefined_value(); |
1881 for (int i = new_length; i < length; i++) { | 1891 for (int i = new_length; i < length; i++) { |
1882 retained_maps->Clear(i, undefined); | 1892 retained_maps->Clear(i, undefined); |
1883 } | 1893 } |
1884 if (new_length != length) retained_maps->SetLength(new_length); | 1894 if (new_length != length) retained_maps->SetLength(new_length); |
1885 ProcessMarkingDeque(); | 1895 ProcessMarkingDeque(); |
1886 } | 1896 } |
1887 | 1897 |
1888 | 1898 |
| 1899 DependentCode* MarkCompactCollector::DependentCodeListFromNonLiveMaps() { |
| 1900 GCTracer::Scope gc_scope(heap()->tracer(), |
| 1901 GCTracer::Scope::MC_EXTRACT_DEPENDENT_CODE); |
| 1902 ArrayList* retained_maps = heap()->retained_maps(); |
| 1903 int length = retained_maps->Length(); |
| 1904 DependentCode* head = DependentCode::cast(heap()->empty_fixed_array()); |
| 1905 for (int i = 0; i < length; i += 2) { |
| 1906 DCHECK(retained_maps->Get(i)->IsWeakCell()); |
| 1907 WeakCell* cell = WeakCell::cast(retained_maps->Get(i)); |
| 1908 DCHECK(!cell->cleared()); |
| 1909 Map* map = Map::cast(cell->value()); |
| 1910 MarkBit map_mark = Marking::MarkBitFrom(map); |
| 1911 if (Marking::IsWhite(map_mark)) { |
| 1912 DependentCode* candidate = map->dependent_code(); |
| 1913 // We rely on the fact that the weak code group comes first. |
| 1914 STATIC_ASSERT(DependentCode::kWeakCodeGroup == 0); |
| 1915 if (candidate->length() > 0 && |
| 1916 candidate->group() == DependentCode::kWeakCodeGroup) { |
| 1917 candidate->set_next_link(head); |
| 1918 head = candidate; |
| 1919 } |
| 1920 } |
| 1921 } |
| 1922 return head; |
| 1923 } |
| 1924 |
| 1925 |
1889 void MarkCompactCollector::EnsureMarkingDequeIsReserved() { | 1926 void MarkCompactCollector::EnsureMarkingDequeIsReserved() { |
1890 DCHECK(!marking_deque_.in_use()); | 1927 DCHECK(!marking_deque_.in_use()); |
1891 if (marking_deque_memory_ == NULL) { | 1928 if (marking_deque_memory_ == NULL) { |
1892 marking_deque_memory_ = new base::VirtualMemory(kMaxMarkingDequeSize); | 1929 marking_deque_memory_ = new base::VirtualMemory(kMaxMarkingDequeSize); |
1893 marking_deque_memory_committed_ = 0; | 1930 marking_deque_memory_committed_ = 0; |
1894 } | 1931 } |
1895 if (marking_deque_memory_ == NULL) { | 1932 if (marking_deque_memory_ == NULL) { |
1896 V8::FatalProcessOutOfMemory("EnsureMarkingDequeIsReserved"); | 1933 V8::FatalProcessOutOfMemory("EnsureMarkingDequeIsReserved"); |
1897 } | 1934 } |
1898 } | 1935 } |
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2185 } | 2222 } |
2186 | 2223 |
2187 // Trim the optimized code map if entries have been removed. | 2224 // Trim the optimized code map if entries have been removed. |
2188 if (new_length < old_length) { | 2225 if (new_length < old_length) { |
2189 shared->TrimOptimizedCodeMap(old_length - new_length); | 2226 shared->TrimOptimizedCodeMap(old_length - new_length); |
2190 } | 2227 } |
2191 } | 2228 } |
2192 } | 2229 } |
2193 | 2230 |
2194 | 2231 |
| 2232 void MarkCompactCollector::ProcessWeakReferences() { |
| 2233 // This should be done before processing weak cells because it checks |
| 2234 // mark bits of maps in weak cells. |
| 2235 DependentCode* dependent_code_list = DependentCodeListFromNonLiveMaps(); |
| 2236 |
| 2237 // Process weak cells before MarkCodeForDeoptimization and |
| 2238 // ClearNonLiveReferences so that weak cells in dependent code arrays are |
| 2239 // cleared or contain only live code objects. |
| 2240 ProcessAndClearWeakCells(); |
| 2241 |
| 2242 MarkDependentCodeListForDeoptimization(dependent_code_list); |
| 2243 |
| 2244 ClearNonLiveReferences(); |
| 2245 |
| 2246 ClearWeakCollections(); |
| 2247 |
| 2248 heap_->set_encountered_weak_cells(Smi::FromInt(0)); |
| 2249 } |
| 2250 |
| 2251 |
2195 void MarkCompactCollector::ClearNonLiveReferences() { | 2252 void MarkCompactCollector::ClearNonLiveReferences() { |
2196 GCTracer::Scope gc_scope(heap()->tracer(), | 2253 GCTracer::Scope gc_scope(heap()->tracer(), |
2197 GCTracer::Scope::MC_NONLIVEREFERENCES); | 2254 GCTracer::Scope::MC_NONLIVEREFERENCES); |
2198 // Iterate over the map space, setting map transitions that go from | 2255 // Iterate over the map space, setting map transitions that go from |
2199 // a marked map to an unmarked map to null transitions. This action | 2256 // a marked map to an unmarked map to null transitions. This action |
2200 // is carried out only on maps of JSObjects and related subtypes. | 2257 // is carried out only on maps of JSObjects and related subtypes. |
2201 HeapObjectIterator map_iterator(heap()->map_space()); | 2258 HeapObjectIterator map_iterator(heap()->map_space()); |
2202 for (HeapObject* obj = map_iterator.Next(); obj != NULL; | 2259 for (HeapObject* obj = map_iterator.Next(); obj != NULL; |
2203 obj = map_iterator.Next()) { | 2260 obj = map_iterator.Next()) { |
2204 Map* map = Map::cast(obj); | 2261 Map* map = Map::cast(obj); |
2205 if (!map->CanTransition()) continue; | 2262 if (!map->CanTransition()) continue; |
2206 MarkBit map_mark = Marking::MarkBitFrom(map); | 2263 MarkBit map_mark = Marking::MarkBitFrom(map); |
2207 bool alive = Marking::IsBlackOrGrey(map_mark); | 2264 bool alive = Marking::IsBlackOrGrey(map_mark); |
2208 if (alive) { | 2265 if (alive) { |
2209 ClearNonLivePrototypeTransitions(map); | 2266 ClearNonLivePrototypeTransitions(map); |
2210 } else { | 2267 } else { |
2211 ClearNonLiveMapTransitions(map); | 2268 ClearNonLiveMapTransitions(map); |
2212 have_code_to_deoptimize_ |= | |
2213 map->dependent_code()->MarkCodeForDeoptimization( | |
2214 isolate(), DependentCode::kWeakCodeGroup); | |
2215 map->set_dependent_code(DependentCode::cast(heap()->empty_fixed_array())); | |
2216 } | 2269 } |
2217 } | 2270 } |
2218 | 2271 |
2219 WeakHashTable* table = heap_->weak_object_to_code_table(); | 2272 WeakHashTable* table = heap_->weak_object_to_code_table(); |
2220 uint32_t capacity = table->Capacity(); | 2273 uint32_t capacity = table->Capacity(); |
2221 for (uint32_t i = 0; i < capacity; i++) { | 2274 for (uint32_t i = 0; i < capacity; i++) { |
2222 uint32_t key_index = table->EntryToIndex(i); | 2275 uint32_t key_index = table->EntryToIndex(i); |
2223 Object* key = table->get(key_index); | 2276 Object* key = table->get(key_index); |
2224 if (!table->IsKey(key)) continue; | 2277 if (!table->IsKey(key)) continue; |
2225 uint32_t value_index = table->EntryToValueIndex(i); | 2278 uint32_t value_index = table->EntryToValueIndex(i); |
2226 Object* value = table->get(value_index); | 2279 Object* value = table->get(value_index); |
2227 DCHECK(key->IsWeakCell()); | 2280 DCHECK(key->IsWeakCell()); |
2228 if (WeakCell::cast(key)->cleared()) { | 2281 if (WeakCell::cast(key)->cleared()) { |
2229 have_code_to_deoptimize_ |= | 2282 have_code_to_deoptimize_ |= |
2230 DependentCode::cast(value)->MarkCodeForDeoptimization( | 2283 DependentCode::cast(value)->MarkCodeForDeoptimization( |
2231 isolate(), DependentCode::kWeakCodeGroup); | 2284 isolate(), DependentCode::kWeakCodeGroup); |
2232 table->set(key_index, heap_->the_hole_value()); | 2285 table->set(key_index, heap_->the_hole_value()); |
2233 table->set(value_index, heap_->the_hole_value()); | 2286 table->set(value_index, heap_->the_hole_value()); |
2234 table->ElementRemoved(); | 2287 table->ElementRemoved(); |
2235 } | 2288 } |
2236 } | 2289 } |
2237 } | 2290 } |
2238 | 2291 |
2239 | 2292 |
| 2293 void MarkCompactCollector::MarkDependentCodeListForDeoptimization( |
| 2294 DependentCode* list_head) { |
| 2295 GCTracer::Scope gc_scope(heap()->tracer(), |
| 2296 GCTracer::Scope::MC_DEOPT_DEPENDENT_CODE); |
| 2297 Isolate* isolate = this->isolate(); |
| 2298 DependentCode* current = list_head; |
| 2299 while (current->length() > 0) { |
| 2300 have_code_to_deoptimize_ |= current->MarkCodeForDeoptimization( |
| 2301 isolate, DependentCode::kWeakCodeGroup); |
| 2302 current = current->next_link(); |
| 2303 } |
| 2304 } |
| 2305 |
| 2306 |
2240 void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) { | 2307 void MarkCompactCollector::ClearNonLivePrototypeTransitions(Map* map) { |
2241 FixedArray* prototype_transitions = | 2308 FixedArray* prototype_transitions = |
2242 TransitionArray::GetPrototypeTransitions(map); | 2309 TransitionArray::GetPrototypeTransitions(map); |
2243 int number_of_transitions = | 2310 int number_of_transitions = |
2244 TransitionArray::NumberOfPrototypeTransitions(prototype_transitions); | 2311 TransitionArray::NumberOfPrototypeTransitions(prototype_transitions); |
2245 | 2312 |
2246 const int header = TransitionArray::kProtoTransitionHeaderSize; | 2313 const int header = TransitionArray::kProtoTransitionHeaderSize; |
2247 int new_number_of_transitions = 0; | 2314 int new_number_of_transitions = 0; |
2248 for (int i = 0; i < number_of_transitions; i++) { | 2315 for (int i = 0; i < number_of_transitions; i++) { |
2249 Object* cell = prototype_transitions->get(header + i); | 2316 Object* cell = prototype_transitions->get(header + i); |
(...skipping 2237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4487 MarkBit mark_bit = Marking::MarkBitFrom(host); | 4554 MarkBit mark_bit = Marking::MarkBitFrom(host); |
4488 if (Marking::IsBlack(mark_bit)) { | 4555 if (Marking::IsBlack(mark_bit)) { |
4489 RelocInfo rinfo(pc, RelocInfo::CODE_TARGET, 0, host); | 4556 RelocInfo rinfo(pc, RelocInfo::CODE_TARGET, 0, host); |
4490 RecordRelocSlot(&rinfo, target); | 4557 RecordRelocSlot(&rinfo, target); |
4491 } | 4558 } |
4492 } | 4559 } |
4493 } | 4560 } |
4494 | 4561 |
4495 } // namespace internal | 4562 } // namespace internal |
4496 } // namespace v8 | 4563 } // namespace v8 |
OLD | NEW |