| 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 |