OLD | NEW |
1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 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/spaces.h" | 5 #include "src/heap/spaces.h" |
6 | 6 |
7 #include "src/base/bits.h" | 7 #include "src/base/bits.h" |
8 #include "src/base/platform/platform.h" | 8 #include "src/base/platform/platform.h" |
9 #include "src/full-codegen/full-codegen.h" | 9 #include "src/full-codegen/full-codegen.h" |
10 #include "src/heap/slot-set.h" | 10 #include "src/heap/slot-set.h" |
(...skipping 1013 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1024 void PagedSpace::TearDown() { | 1024 void PagedSpace::TearDown() { |
1025 PageIterator iterator(this); | 1025 PageIterator iterator(this); |
1026 while (iterator.has_next()) { | 1026 while (iterator.has_next()) { |
1027 heap()->isolate()->memory_allocator()->Free(iterator.next()); | 1027 heap()->isolate()->memory_allocator()->Free(iterator.next()); |
1028 } | 1028 } |
1029 anchor_.set_next_page(&anchor_); | 1029 anchor_.set_next_page(&anchor_); |
1030 anchor_.set_prev_page(&anchor_); | 1030 anchor_.set_prev_page(&anchor_); |
1031 accounting_stats_.Clear(); | 1031 accounting_stats_.Clear(); |
1032 } | 1032 } |
1033 | 1033 |
1034 | |
1035 void PagedSpace::AddMemory(Address start, intptr_t size) { | |
1036 accounting_stats_.ExpandSpace(static_cast<int>(size)); | |
1037 Free(start, static_cast<int>(size)); | |
1038 } | |
1039 | |
1040 | |
1041 void PagedSpace::RefillFreeList() { | 1034 void PagedSpace::RefillFreeList() { |
1042 MarkCompactCollector* collector = heap()->mark_compact_collector(); | 1035 // Any PagedSpace might invoke RefillFreeList. We filter all but our old |
1043 FreeList* free_list = nullptr; | 1036 // generation spaces out. |
1044 if (this == heap()->old_space()) { | 1037 if (identity() != OLD_SPACE && identity() != CODE_SPACE && |
1045 free_list = collector->free_list_old_space().get(); | 1038 identity() != MAP_SPACE) { |
1046 } else if (this == heap()->code_space()) { | |
1047 free_list = collector->free_list_code_space().get(); | |
1048 } else if (this == heap()->map_space()) { | |
1049 free_list = collector->free_list_map_space().get(); | |
1050 } else { | |
1051 // Any PagedSpace might invoke RefillFreeList. We filter all but our old | |
1052 // generation spaces out. | |
1053 return; | 1039 return; |
1054 } | 1040 } |
1055 DCHECK(free_list != nullptr); | 1041 MarkCompactCollector* collector = heap()->mark_compact_collector(); |
1056 intptr_t added = free_list_.Concatenate(free_list); | 1042 List<Page*>* swept_pages = collector->swept_pages(identity()); |
| 1043 intptr_t added = 0; |
| 1044 { |
| 1045 base::LockGuard<base::Mutex> guard(collector->swept_pages_mutex()); |
| 1046 for (int i = swept_pages->length() - 1; i >= 0; --i) { |
| 1047 Page* p = (*swept_pages)[i]; |
| 1048 // Only during compaction pages can actually change ownership. This is |
| 1049 // safe because there exists no other competing action on the page links |
| 1050 // during compaction. |
| 1051 if (is_local() && (p->owner() != this)) { |
| 1052 if (added > kCompactionMemoryWanted) break; |
| 1053 base::LockGuard<base::Mutex> guard( |
| 1054 reinterpret_cast<PagedSpace*>(p->owner())->mutex()); |
| 1055 p->Unlink(); |
| 1056 p->set_owner(this); |
| 1057 p->InsertAfter(anchor_.prev_page()); |
| 1058 } |
| 1059 added += RelinkFreeListCategories(p); |
| 1060 added += p->wasted_memory(); |
| 1061 swept_pages->Remove(i); |
| 1062 } |
| 1063 } |
1057 accounting_stats_.IncreaseCapacity(added); | 1064 accounting_stats_.IncreaseCapacity(added); |
1058 } | 1065 } |
1059 | 1066 |
1060 | 1067 void PagedSpace::MergeCompactionSpace(CompactionSpace* other) { |
1061 void CompactionSpace::RefillFreeList() { | |
1062 MarkCompactCollector* collector = heap()->mark_compact_collector(); | |
1063 FreeList* free_list = nullptr; | |
1064 if (identity() == OLD_SPACE) { | |
1065 free_list = collector->free_list_old_space().get(); | |
1066 } else if (identity() == CODE_SPACE) { | |
1067 free_list = collector->free_list_code_space().get(); | |
1068 } else { | |
1069 // Compaction spaces only represent old or code space. | |
1070 UNREACHABLE(); | |
1071 } | |
1072 DCHECK(free_list != nullptr); | |
1073 intptr_t refilled = 0; | |
1074 while (refilled < kCompactionMemoryWanted) { | |
1075 FreeSpace* node = | |
1076 free_list->TryRemoveMemory(kCompactionMemoryWanted - refilled); | |
1077 if (node == nullptr) return; | |
1078 refilled += node->size(); | |
1079 AddMemory(node->address(), node->size()); | |
1080 } | |
1081 } | |
1082 | |
1083 void PagedSpace::MoveOverFreeMemory(PagedSpace* other) { | |
1084 DCHECK(identity() == other->identity()); | 1068 DCHECK(identity() == other->identity()); |
1085 // Destroy the linear allocation space of {other}. This is needed to | |
1086 // (a) not waste the memory and | |
1087 // (b) keep the rest of the chunk in an iterable state (filler is needed). | |
1088 other->EmptyAllocationInfo(); | |
1089 | |
1090 // Move over the free list. Concatenate makes sure that the source free list | |
1091 // gets properly reset after moving over all nodes. | |
1092 intptr_t added = free_list_.Concatenate(other->free_list()); | |
1093 | |
1094 // Moved memory is not recorded as allocated memory, but rather increases and | |
1095 // decreases capacity of the corresponding spaces. | |
1096 other->accounting_stats_.DecreaseCapacity(added); | |
1097 accounting_stats_.IncreaseCapacity(added); | |
1098 } | |
1099 | |
1100 | |
1101 void PagedSpace::MergeCompactionSpace(CompactionSpace* other) { | |
1102 // Unmerged fields: | 1069 // Unmerged fields: |
1103 // area_size_ | 1070 // area_size_ |
1104 // anchor_ | 1071 // anchor_ |
1105 | 1072 |
1106 MoveOverFreeMemory(other); | 1073 other->EmptyAllocationInfo(); |
1107 | 1074 |
1108 // Update and clear accounting statistics. | 1075 // Update and clear accounting statistics. |
1109 accounting_stats_.Merge(other->accounting_stats_); | 1076 accounting_stats_.Merge(other->accounting_stats_); |
1110 other->accounting_stats_.Clear(); | 1077 other->accounting_stats_.Clear(); |
1111 | 1078 |
1112 // The linear allocation area of {other} should be destroyed now. | 1079 // The linear allocation area of {other} should be destroyed now. |
1113 DCHECK(other->top() == nullptr); | 1080 DCHECK(other->top() == nullptr); |
1114 DCHECK(other->limit() == nullptr); | 1081 DCHECK(other->limit() == nullptr); |
1115 | 1082 |
1116 AccountCommitted(other->CommittedMemory()); | 1083 AccountCommitted(other->CommittedMemory()); |
1117 | 1084 |
1118 // Move over pages. | 1085 // Move over pages. |
1119 PageIterator it(other); | 1086 PageIterator it(other); |
1120 Page* p = nullptr; | 1087 Page* p = nullptr; |
1121 while (it.has_next()) { | 1088 while (it.has_next()) { |
1122 p = it.next(); | 1089 p = it.next(); |
| 1090 |
| 1091 // Relinking requires the category to be unlinked. |
| 1092 other->UnlinkFreeListCategories(p); |
| 1093 |
1123 p->Unlink(); | 1094 p->Unlink(); |
1124 p->set_owner(this); | 1095 p->set_owner(this); |
1125 p->InsertAfter(anchor_.prev_page()); | 1096 p->InsertAfter(anchor_.prev_page()); |
| 1097 RelinkFreeListCategories(p); |
1126 } | 1098 } |
1127 } | 1099 } |
1128 | 1100 |
1129 | 1101 |
1130 size_t PagedSpace::CommittedPhysicalMemory() { | 1102 size_t PagedSpace::CommittedPhysicalMemory() { |
1131 if (!base::VirtualMemory::HasLazyCommits()) return CommittedMemory(); | 1103 if (!base::VirtualMemory::HasLazyCommits()) return CommittedMemory(); |
1132 MemoryChunk::UpdateHighWaterMark(allocation_info_.top()); | 1104 MemoryChunk::UpdateHighWaterMark(allocation_info_.top()); |
1133 size_t size = 0; | 1105 size_t size = 0; |
1134 PageIterator it(this); | 1106 PageIterator it(this); |
1135 while (it.has_next()) { | 1107 while (it.has_next()) { |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1222 Page* page = page_iterator.next(); | 1194 Page* page = page_iterator.next(); |
1223 page->ResetFreeListStatistics(); | 1195 page->ResetFreeListStatistics(); |
1224 } | 1196 } |
1225 } | 1197 } |
1226 | 1198 |
1227 | 1199 |
1228 void PagedSpace::IncreaseCapacity(int size) { | 1200 void PagedSpace::IncreaseCapacity(int size) { |
1229 accounting_stats_.ExpandSpace(size); | 1201 accounting_stats_.ExpandSpace(size); |
1230 } | 1202 } |
1231 | 1203 |
| 1204 void PagedSpace::ReleasePage(Page* page) { |
| 1205 DCHECK_EQ(page->LiveBytes(), 0); |
| 1206 DCHECK_EQ(AreaSize(), page->area_size()); |
| 1207 DCHECK_EQ(page->owner(), this); |
1232 | 1208 |
1233 void PagedSpace::ReleasePage(Page* page, bool evict_free_list_items) { | 1209 free_list_.EvictFreeListItems(page); |
1234 DCHECK(page->LiveBytes() == 0); | |
1235 DCHECK(AreaSize() == page->area_size()); | |
1236 | |
1237 if (evict_free_list_items) { | |
1238 intptr_t size = free_list_.EvictFreeListItems(page); | |
1239 accounting_stats_.AllocateBytes(size); | |
1240 DCHECK_EQ(AreaSize(), static_cast<int>(size)); | |
1241 } | |
1242 | |
1243 DCHECK(!free_list_.ContainsPageFreeListItems(page)); | 1210 DCHECK(!free_list_.ContainsPageFreeListItems(page)); |
1244 | 1211 |
1245 if (Page::FromAllocationTop(allocation_info_.top()) == page) { | 1212 if (Page::FromAllocationTop(allocation_info_.top()) == page) { |
1246 allocation_info_.Reset(nullptr, nullptr); | 1213 allocation_info_.Reset(nullptr, nullptr); |
1247 } | 1214 } |
1248 | 1215 |
1249 // If page is still in a list, unlink it from that list. | 1216 // If page is still in a list, unlink it from that list. |
1250 if (page->next_chunk() != NULL) { | 1217 if (page->next_chunk() != NULL) { |
1251 DCHECK(page->prev_chunk() != NULL); | 1218 DCHECK(page->prev_chunk() != NULL); |
1252 page->Unlink(); | 1219 page->Unlink(); |
1253 } | 1220 } |
1254 | 1221 |
1255 AccountUncommitted(static_cast<intptr_t>(page->size())); | 1222 AccountUncommitted(static_cast<intptr_t>(page->size())); |
1256 heap()->QueueMemoryChunkForFree(page); | 1223 heap()->QueueMemoryChunkForFree(page); |
1257 | 1224 |
1258 DCHECK(Capacity() > 0); | 1225 DCHECK(Capacity() > 0); |
1259 accounting_stats_.ShrinkSpace(AreaSize()); | 1226 accounting_stats_.ShrinkSpace(AreaSize()); |
1260 } | 1227 } |
1261 | 1228 |
1262 | |
1263 #ifdef DEBUG | 1229 #ifdef DEBUG |
1264 void PagedSpace::Print() {} | 1230 void PagedSpace::Print() {} |
1265 #endif | 1231 #endif |
1266 | 1232 |
1267 #ifdef VERIFY_HEAP | 1233 #ifdef VERIFY_HEAP |
1268 void PagedSpace::Verify(ObjectVisitor* visitor) { | 1234 void PagedSpace::Verify(ObjectVisitor* visitor) { |
1269 bool allocation_pointer_found_in_space = | 1235 bool allocation_pointer_found_in_space = |
1270 (allocation_info_.top() == allocation_info_.limit()); | 1236 (allocation_info_.top() == allocation_info_.limit()); |
1271 PageIterator page_iterator(this); | 1237 PageIterator page_iterator(this); |
1272 while (page_iterator.has_next()) { | 1238 while (page_iterator.has_next()) { |
(...skipping 885 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2158 if (from_space_.is_committed()) { | 2124 if (from_space_.is_committed()) { |
2159 size += from_space_.CommittedPhysicalMemory(); | 2125 size += from_space_.CommittedPhysicalMemory(); |
2160 } | 2126 } |
2161 return size; | 2127 return size; |
2162 } | 2128 } |
2163 | 2129 |
2164 | 2130 |
2165 // ----------------------------------------------------------------------------- | 2131 // ----------------------------------------------------------------------------- |
2166 // Free lists for old object spaces implementation | 2132 // Free lists for old object spaces implementation |
2167 | 2133 |
2168 intptr_t FreeListCategory::Concatenate(FreeListCategory* category) { | |
2169 intptr_t free_bytes = 0; | |
2170 if (category->top() != NULL) { | |
2171 DCHECK(category->end_ != NULL); | |
2172 free_bytes = category->available(); | |
2173 if (end_ == NULL) { | |
2174 end_ = category->end(); | |
2175 } else { | |
2176 category->end()->set_next(top()); | |
2177 } | |
2178 set_top(category->top()); | |
2179 available_ += category->available(); | |
2180 category->Reset(); | |
2181 } | |
2182 return free_bytes; | |
2183 } | |
2184 | |
2185 | 2134 |
2186 void FreeListCategory::Reset() { | 2135 void FreeListCategory::Reset() { |
2187 set_top(nullptr); | 2136 set_top(nullptr); |
2188 set_end(nullptr); | 2137 set_prev(nullptr); |
| 2138 set_next(nullptr); |
2189 available_ = 0; | 2139 available_ = 0; |
2190 } | 2140 } |
2191 | 2141 |
| 2142 FreeSpace* FreeListCategory::PickNodeFromList(int* node_size) { |
| 2143 DCHECK(page()->CanAllocate()); |
2192 | 2144 |
2193 intptr_t FreeListCategory::EvictFreeListItemsInList(Page* p) { | |
2194 intptr_t sum = 0; | |
2195 FreeSpace* prev_node = nullptr; | |
2196 for (FreeSpace* cur_node = top(); cur_node != nullptr; | |
2197 cur_node = cur_node->next()) { | |
2198 Page* page_for_node = Page::FromAddress(cur_node->address()); | |
2199 if (page_for_node == p) { | |
2200 // FreeSpace node on eviction page found, unlink it. | |
2201 int size = cur_node->size(); | |
2202 sum += size; | |
2203 DCHECK((prev_node != nullptr) || (top() == cur_node)); | |
2204 if (cur_node == top()) { | |
2205 set_top(cur_node->next()); | |
2206 } | |
2207 if (cur_node == end()) { | |
2208 set_end(prev_node); | |
2209 } | |
2210 if (prev_node != nullptr) { | |
2211 prev_node->set_next(cur_node->next()); | |
2212 } | |
2213 continue; | |
2214 } | |
2215 prev_node = cur_node; | |
2216 } | |
2217 p->add_available_in_free_list(-sum); | |
2218 available_ -= sum; | |
2219 return sum; | |
2220 } | |
2221 | |
2222 | |
2223 bool FreeListCategory::ContainsPageFreeListItemsInList(Page* p) { | |
2224 FreeSpace* node = top(); | |
2225 while (node != NULL) { | |
2226 if (Page::FromAddress(node->address()) == p) return true; | |
2227 node = node->next(); | |
2228 } | |
2229 return false; | |
2230 } | |
2231 | |
2232 | |
2233 FreeSpace* FreeListCategory::PickNodeFromList(int* node_size) { | |
2234 FreeSpace* node = top(); | 2145 FreeSpace* node = top(); |
2235 if (node == nullptr) return nullptr; | 2146 if (node == nullptr) return nullptr; |
2236 | 2147 set_top(node->next()); |
2237 Page* page = Page::FromAddress(node->address()); | 2148 *node_size = node->Size(); |
2238 while ((node != nullptr) && !page->CanAllocate()) { | 2149 available_ -= *node_size; |
2239 available_ -= node->size(); | |
2240 page->add_available_in_free_list(-(node->Size())); | |
2241 node = node->next(); | |
2242 } | |
2243 | |
2244 if (node != nullptr) { | |
2245 set_top(node->next()); | |
2246 *node_size = node->Size(); | |
2247 available_ -= *node_size; | |
2248 } else { | |
2249 set_top(nullptr); | |
2250 } | |
2251 | |
2252 if (top() == nullptr) { | |
2253 set_end(nullptr); | |
2254 } | |
2255 | |
2256 return node; | 2150 return node; |
2257 } | 2151 } |
2258 | 2152 |
| 2153 FreeSpace* FreeListCategory::TryPickNodeFromList(int minimum_size, |
| 2154 int* node_size) { |
| 2155 DCHECK(page()->CanAllocate()); |
2259 | 2156 |
2260 FreeSpace* FreeListCategory::PickNodeFromList(int size_in_bytes, | |
2261 int* node_size) { | |
2262 FreeSpace* node = PickNodeFromList(node_size); | 2157 FreeSpace* node = PickNodeFromList(node_size); |
2263 if ((node != nullptr) && (*node_size < size_in_bytes)) { | 2158 if ((node != nullptr) && (*node_size < minimum_size)) { |
2264 Free(node, *node_size); | 2159 Free(node, *node_size, kLinkCategory); |
2265 *node_size = 0; | 2160 *node_size = 0; |
2266 return nullptr; | 2161 return nullptr; |
2267 } | 2162 } |
2268 return node; | 2163 return node; |
2269 } | 2164 } |
2270 | 2165 |
| 2166 FreeSpace* FreeListCategory::SearchForNodeInList(int minimum_size, |
| 2167 int* node_size) { |
| 2168 DCHECK(page()->CanAllocate()); |
2271 | 2169 |
2272 FreeSpace* FreeListCategory::SearchForNodeInList(int size_in_bytes, | |
2273 int* node_size) { | |
2274 FreeSpace* prev_non_evac_node = nullptr; | 2170 FreeSpace* prev_non_evac_node = nullptr; |
2275 for (FreeSpace* cur_node = top(); cur_node != nullptr; | 2171 for (FreeSpace* cur_node = top(); cur_node != nullptr; |
2276 cur_node = cur_node->next()) { | 2172 cur_node = cur_node->next()) { |
2277 int size = cur_node->size(); | 2173 int size = cur_node->size(); |
2278 Page* page_for_node = Page::FromAddress(cur_node->address()); | 2174 if (size >= minimum_size) { |
2279 | |
2280 if ((size >= size_in_bytes) || !page_for_node->CanAllocate()) { | |
2281 // The node is either large enough or contained in an evacuation | |
2282 // candidate. In both cases we need to unlink it from the list. | |
2283 available_ -= size; | 2175 available_ -= size; |
2284 if (cur_node == top()) { | 2176 if (cur_node == top()) { |
2285 set_top(cur_node->next()); | 2177 set_top(cur_node->next()); |
2286 } | 2178 } |
2287 if (cur_node == end()) { | |
2288 set_end(prev_non_evac_node); | |
2289 } | |
2290 if (prev_non_evac_node != nullptr) { | 2179 if (prev_non_evac_node != nullptr) { |
2291 prev_non_evac_node->set_next(cur_node->next()); | 2180 prev_non_evac_node->set_next(cur_node->next()); |
2292 } | 2181 } |
2293 // For evacuation candidates we continue. | |
2294 if (!page_for_node->CanAllocate()) { | |
2295 page_for_node->add_available_in_free_list(-size); | |
2296 continue; | |
2297 } | |
2298 // Otherwise we have a large enough node and can return. | |
2299 *node_size = size; | 2182 *node_size = size; |
2300 return cur_node; | 2183 return cur_node; |
2301 } | 2184 } |
2302 | 2185 |
2303 prev_non_evac_node = cur_node; | 2186 prev_non_evac_node = cur_node; |
2304 } | 2187 } |
2305 return nullptr; | 2188 return nullptr; |
2306 } | 2189 } |
2307 | 2190 |
| 2191 bool FreeListCategory::Free(FreeSpace* free_space, int size_in_bytes, |
| 2192 FreeMode mode) { |
| 2193 if (!page()->CanAllocate()) return false; |
2308 | 2194 |
2309 void FreeListCategory::Free(FreeSpace* free_space, int size_in_bytes) { | |
2310 free_space->set_next(top()); | 2195 free_space->set_next(top()); |
2311 set_top(free_space); | 2196 set_top(free_space); |
2312 if (end_ == NULL) { | 2197 available_ += size_in_bytes; |
2313 end_ = free_space; | 2198 if ((mode == kLinkCategory) && (prev() == nullptr) && (next() == nullptr)) { |
| 2199 owner()->AddCategory(this); |
2314 } | 2200 } |
2315 available_ += size_in_bytes; | 2201 return true; |
2316 } | 2202 } |
2317 | 2203 |
2318 | 2204 |
2319 void FreeListCategory::RepairFreeList(Heap* heap) { | 2205 void FreeListCategory::RepairFreeList(Heap* heap) { |
2320 FreeSpace* n = top(); | 2206 FreeSpace* n = top(); |
2321 while (n != NULL) { | 2207 while (n != NULL) { |
2322 Map** map_location = reinterpret_cast<Map**>(n->address()); | 2208 Map** map_location = reinterpret_cast<Map**>(n->address()); |
2323 if (*map_location == NULL) { | 2209 if (*map_location == NULL) { |
2324 *map_location = heap->free_space_map(); | 2210 *map_location = heap->free_space_map(); |
2325 } else { | 2211 } else { |
2326 DCHECK(*map_location == heap->free_space_map()); | 2212 DCHECK(*map_location == heap->free_space_map()); |
2327 } | 2213 } |
2328 n = n->next(); | 2214 n = n->next(); |
2329 } | 2215 } |
2330 } | 2216 } |
2331 | 2217 |
| 2218 void FreeListCategory::Relink() { |
| 2219 DCHECK(!is_linked()); |
| 2220 owner()->AddCategory(this); |
| 2221 } |
| 2222 |
| 2223 void FreeListCategory::Invalidate() { |
| 2224 page()->add_available_in_free_list(-available()); |
| 2225 Reset(); |
| 2226 type_ = kInvalidCategory; |
| 2227 } |
| 2228 |
2332 FreeList::FreeList(PagedSpace* owner) : owner_(owner), wasted_bytes_(0) { | 2229 FreeList::FreeList(PagedSpace* owner) : owner_(owner), wasted_bytes_(0) { |
2333 for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | 2230 for (int i = kFirstCategory; i < kNumberOfCategories; i++) { |
2334 category_[i].Initialize(this, static_cast<FreeListCategoryType>(i)); | 2231 categories_[i] = nullptr; |
2335 } | 2232 } |
2336 Reset(); | 2233 Reset(); |
2337 } | 2234 } |
2338 | 2235 |
2339 | 2236 |
2340 intptr_t FreeList::Concatenate(FreeList* other) { | 2237 void FreeList::Reset() { |
2341 intptr_t usable_bytes = 0; | 2238 ForAllFreeListCategories( |
2342 intptr_t wasted_bytes = 0; | 2239 [](FreeListCategory* category) { category->Reset(); }); |
2343 | |
2344 // This is safe (not going to deadlock) since Concatenate operations | |
2345 // are never performed on the same free lists at the same time in | |
2346 // reverse order. Furthermore, we only lock if the PagedSpace containing | |
2347 // the free list is know to be globally available, i.e., not local. | |
2348 if (!owner()->is_local()) mutex_.Lock(); | |
2349 if (!other->owner()->is_local()) other->mutex()->Lock(); | |
2350 | |
2351 wasted_bytes = other->wasted_bytes_; | |
2352 wasted_bytes_ += wasted_bytes; | |
2353 other->wasted_bytes_ = 0; | |
2354 | |
2355 for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | 2240 for (int i = kFirstCategory; i < kNumberOfCategories; i++) { |
2356 usable_bytes += category_[i].Concatenate( | 2241 categories_[i] = nullptr; |
2357 other->GetFreeListCategory(static_cast<FreeListCategoryType>(i))); | |
2358 } | |
2359 | |
2360 if (!other->owner()->is_local()) other->mutex()->Unlock(); | |
2361 if (!owner()->is_local()) mutex_.Unlock(); | |
2362 return usable_bytes + wasted_bytes; | |
2363 } | |
2364 | |
2365 | |
2366 void FreeList::Reset() { | |
2367 for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | |
2368 category_[i].Reset(); | |
2369 } | 2242 } |
2370 ResetStats(); | 2243 ResetStats(); |
2371 } | 2244 } |
2372 | 2245 |
2373 | 2246 int FreeList::Free(Address start, int size_in_bytes, FreeMode mode) { |
2374 int FreeList::Free(Address start, int size_in_bytes) { | |
2375 if (size_in_bytes == 0) return 0; | 2247 if (size_in_bytes == 0) return 0; |
2376 | 2248 |
2377 owner()->heap()->CreateFillerObjectAt(start, size_in_bytes, | 2249 owner()->heap()->CreateFillerObjectAt(start, size_in_bytes, |
2378 ClearRecordedSlots::kNo); | 2250 ClearRecordedSlots::kNo); |
2379 | 2251 |
2380 Page* page = Page::FromAddress(start); | 2252 Page* page = Page::FromAddress(start); |
2381 | 2253 |
2382 // Blocks have to be a minimum size to hold free list items. | 2254 // Blocks have to be a minimum size to hold free list items. |
2383 if (size_in_bytes < kMinBlockSize) { | 2255 if (size_in_bytes < kMinBlockSize) { |
2384 page->add_wasted_memory(size_in_bytes); | 2256 page->add_wasted_memory(size_in_bytes); |
2385 wasted_bytes_ += size_in_bytes; | 2257 wasted_bytes_.Increment(size_in_bytes); |
2386 return size_in_bytes; | 2258 return size_in_bytes; |
2387 } | 2259 } |
2388 | 2260 |
2389 FreeSpace* free_space = FreeSpace::cast(HeapObject::FromAddress(start)); | 2261 FreeSpace* free_space = FreeSpace::cast(HeapObject::FromAddress(start)); |
2390 // Insert other blocks at the head of a free list of the appropriate | 2262 // Insert other blocks at the head of a free list of the appropriate |
2391 // magnitude. | 2263 // magnitude. |
2392 FreeListCategoryType type = SelectFreeListCategoryType(size_in_bytes); | 2264 FreeListCategoryType type = SelectFreeListCategoryType(size_in_bytes); |
2393 category_[type].Free(free_space, size_in_bytes); | 2265 if (page->free_list_category(type)->Free(free_space, size_in_bytes, mode)) { |
2394 page->add_available_in_free_list(size_in_bytes); | 2266 page->add_available_in_free_list(size_in_bytes); |
2395 | 2267 } |
2396 DCHECK(IsVeryLong() || Available() == SumFreeLists()); | |
2397 return 0; | 2268 return 0; |
2398 } | 2269 } |
2399 | 2270 |
| 2271 FreeSpace* FreeList::FindNodeIn(FreeListCategoryType type, int* node_size) { |
| 2272 FreeListCategoryIterator it(this, type); |
| 2273 FreeSpace* node = nullptr; |
| 2274 while (it.HasNext()) { |
| 2275 FreeListCategory* current = it.Next(); |
| 2276 node = current->PickNodeFromList(node_size); |
| 2277 if (node != nullptr) { |
| 2278 Page::FromAddress(node->address()) |
| 2279 ->add_available_in_free_list(-(*node_size)); |
| 2280 DCHECK(IsVeryLong() || Available() == SumFreeLists()); |
| 2281 return node; |
| 2282 } |
| 2283 } |
| 2284 return node; |
| 2285 } |
2400 | 2286 |
2401 FreeSpace* FreeList::FindNodeIn(FreeListCategoryType category, int* node_size) { | 2287 FreeSpace* FreeList::TryFindNodeIn(FreeListCategoryType type, int* node_size, |
2402 FreeSpace* node = GetFreeListCategory(category)->PickNodeFromList(node_size); | 2288 int minimum_size) { |
| 2289 if (categories_[type] == nullptr) return nullptr; |
| 2290 FreeSpace* node = |
| 2291 categories_[type]->TryPickNodeFromList(minimum_size, node_size); |
2403 if (node != nullptr) { | 2292 if (node != nullptr) { |
2404 Page::FromAddress(node->address()) | 2293 Page::FromAddress(node->address()) |
2405 ->add_available_in_free_list(-(*node_size)); | 2294 ->add_available_in_free_list(-(*node_size)); |
2406 DCHECK(IsVeryLong() || Available() == SumFreeLists()); | 2295 DCHECK(IsVeryLong() || Available() == SumFreeLists()); |
2407 } | 2296 } |
2408 return node; | 2297 return node; |
2409 } | 2298 } |
2410 | 2299 |
| 2300 FreeSpace* FreeList::SearchForNodeInList(FreeListCategoryType type, |
| 2301 int* node_size, int minimum_size) { |
| 2302 FreeListCategoryIterator it(this, type); |
| 2303 FreeSpace* node = nullptr; |
| 2304 while (it.HasNext()) { |
| 2305 FreeListCategory* current = it.Next(); |
| 2306 node = current->SearchForNodeInList(minimum_size, node_size); |
| 2307 if (node != nullptr) { |
| 2308 Page::FromAddress(node->address()) |
| 2309 ->add_available_in_free_list(-(*node_size)); |
| 2310 DCHECK(IsVeryLong() || Available() == SumFreeLists()); |
| 2311 return node; |
| 2312 } |
| 2313 } |
| 2314 return node; |
| 2315 } |
2411 | 2316 |
2412 FreeSpace* FreeList::FindNodeFor(int size_in_bytes, int* node_size) { | 2317 FreeSpace* FreeList::FindNodeFor(int size_in_bytes, int* node_size) { |
2413 FreeSpace* node = nullptr; | 2318 FreeSpace* node = nullptr; |
2414 Page* page = nullptr; | 2319 Page* page = nullptr; |
2415 | 2320 |
2416 // First try the allocation fast path: try to allocate the minimum element | 2321 // First try the allocation fast path: try to allocate the minimum element |
2417 // size of a free list category. This operation is constant time. | 2322 // size of a free list category. This operation is constant time. |
2418 FreeListCategoryType type = | 2323 FreeListCategoryType type = |
2419 SelectFastAllocationFreeListCategoryType(size_in_bytes); | 2324 SelectFastAllocationFreeListCategoryType(size_in_bytes); |
2420 for (int i = type; i < kHuge; i++) { | 2325 for (int i = type; i < kHuge; i++) { |
2421 node = FindNodeIn(static_cast<FreeListCategoryType>(i), node_size); | 2326 node = FindNodeIn(static_cast<FreeListCategoryType>(i), node_size); |
2422 if (node != nullptr) return node; | 2327 if (node != nullptr) return node; |
2423 } | 2328 } |
2424 | 2329 |
2425 // Next search the huge list for free list nodes. This takes linear time in | 2330 // Next search the huge list for free list nodes. This takes linear time in |
2426 // the number of huge elements. | 2331 // the number of huge elements. |
2427 node = category_[kHuge].SearchForNodeInList(size_in_bytes, node_size); | 2332 node = SearchForNodeInList(kHuge, node_size, size_in_bytes); |
2428 if (node != nullptr) { | 2333 if (node != nullptr) { |
2429 page = Page::FromAddress(node->address()); | |
2430 page->add_available_in_free_list(-(*node_size)); | |
2431 DCHECK(IsVeryLong() || Available() == SumFreeLists()); | 2334 DCHECK(IsVeryLong() || Available() == SumFreeLists()); |
2432 return node; | 2335 return node; |
2433 } | 2336 } |
2434 | 2337 |
2435 // We need a huge block of memory, but we didn't find anything in the huge | 2338 // We need a huge block of memory, but we didn't find anything in the huge |
2436 // list. | 2339 // list. |
2437 if (type == kHuge) return nullptr; | 2340 if (type == kHuge) return nullptr; |
2438 | 2341 |
2439 // Now search the best fitting free list for a node that has at least the | 2342 // Now search the best fitting free list for a node that has at least the |
2440 // requested size. | 2343 // requested size. |
2441 type = SelectFreeListCategoryType(size_in_bytes); | 2344 type = SelectFreeListCategoryType(size_in_bytes); |
2442 node = category_[type].PickNodeFromList(size_in_bytes, node_size); | 2345 node = TryFindNodeIn(type, node_size, size_in_bytes); |
2443 if (node != nullptr) { | 2346 if (node != nullptr) { |
2444 DCHECK(size_in_bytes <= *node_size); | 2347 DCHECK(size_in_bytes <= *node_size); |
2445 page = Page::FromAddress(node->address()); | 2348 page = Page::FromAddress(node->address()); |
2446 page->add_available_in_free_list(-(*node_size)); | 2349 page->add_available_in_free_list(-(*node_size)); |
2447 } | 2350 } |
2448 | 2351 |
2449 DCHECK(IsVeryLong() || Available() == SumFreeLists()); | 2352 DCHECK(IsVeryLong() || Available() == SumFreeLists()); |
2450 return node; | 2353 return node; |
2451 } | 2354 } |
2452 | 2355 |
2453 | |
2454 FreeSpace* FreeList::TryRemoveMemory(intptr_t hint_size_in_bytes) { | |
2455 hint_size_in_bytes = RoundDown(hint_size_in_bytes, kPointerSize); | |
2456 base::LockGuard<base::Mutex> guard(&mutex_); | |
2457 FreeSpace* node = nullptr; | |
2458 int node_size = 0; | |
2459 // Try to find a node that fits exactly. | |
2460 node = FindNodeFor(static_cast<int>(hint_size_in_bytes), &node_size); | |
2461 // If no node could be found get as much memory as possible. | |
2462 if (node == nullptr) node = FindNodeIn(kHuge, &node_size); | |
2463 if (node == nullptr) node = FindNodeIn(kLarge, &node_size); | |
2464 if (node != nullptr) { | |
2465 // We round up the size to (kMinBlockSize + kPointerSize) to (a) have a | |
2466 // size larger then the minimum size required for FreeSpace, and (b) to get | |
2467 // a block that can actually be freed into some FreeList later on. | |
2468 if (hint_size_in_bytes <= kMinBlockSize) { | |
2469 hint_size_in_bytes = kMinBlockSize + kPointerSize; | |
2470 } | |
2471 // Give back left overs that were not required by {size_in_bytes}. | |
2472 intptr_t left_over = node_size - hint_size_in_bytes; | |
2473 | |
2474 // Do not bother to return anything below {kMinBlockSize} as it would be | |
2475 // immediately discarded anyways. | |
2476 if (left_over > kMinBlockSize) { | |
2477 Free(node->address() + hint_size_in_bytes, static_cast<int>(left_over)); | |
2478 node->set_size(static_cast<int>(hint_size_in_bytes)); | |
2479 } | |
2480 } | |
2481 return node; | |
2482 } | |
2483 | |
2484 | |
2485 // Allocation on the old space free list. If it succeeds then a new linear | 2356 // Allocation on the old space free list. If it succeeds then a new linear |
2486 // allocation space has been set up with the top and limit of the space. If | 2357 // allocation space has been set up with the top and limit of the space. If |
2487 // the allocation fails then NULL is returned, and the caller can perform a GC | 2358 // the allocation fails then NULL is returned, and the caller can perform a GC |
2488 // or allocate a new page before retrying. | 2359 // or allocate a new page before retrying. |
2489 HeapObject* FreeList::Allocate(int size_in_bytes) { | 2360 HeapObject* FreeList::Allocate(int size_in_bytes) { |
2490 DCHECK(0 < size_in_bytes); | 2361 DCHECK(0 < size_in_bytes); |
2491 DCHECK(size_in_bytes <= kMaxBlockSize); | 2362 DCHECK(size_in_bytes <= kMaxBlockSize); |
2492 DCHECK(IsAligned(size_in_bytes, kPointerSize)); | 2363 DCHECK(IsAligned(size_in_bytes, kPointerSize)); |
2493 // Don't free list allocate if there is linear space available. | 2364 // Don't free list allocate if there is linear space available. |
2494 DCHECK(owner_->limit() - owner_->top() < size_in_bytes); | 2365 DCHECK(owner_->limit() - owner_->top() < size_in_bytes); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2548 } else if (bytes_left > 0) { | 2419 } else if (bytes_left > 0) { |
2549 // Normally we give the rest of the node to the allocator as its new | 2420 // Normally we give the rest of the node to the allocator as its new |
2550 // linear allocation area. | 2421 // linear allocation area. |
2551 owner_->SetTopAndLimit(new_node->address() + size_in_bytes, | 2422 owner_->SetTopAndLimit(new_node->address() + size_in_bytes, |
2552 new_node->address() + new_node_size); | 2423 new_node->address() + new_node_size); |
2553 } | 2424 } |
2554 | 2425 |
2555 return new_node; | 2426 return new_node; |
2556 } | 2427 } |
2557 | 2428 |
2558 | 2429 intptr_t FreeList::EvictFreeListItems(Page* page) { |
2559 intptr_t FreeList::EvictFreeListItems(Page* p) { | 2430 intptr_t sum = 0; |
2560 intptr_t sum = category_[kHuge].EvictFreeListItemsInList(p); | 2431 page->ForAllFreeListCategories( |
2561 if (sum < p->area_size()) { | 2432 [this, &sum, page](FreeListCategory* category) { |
2562 for (int i = kFirstCategory; i <= kLarge; i++) { | 2433 DCHECK_EQ(this, category->owner()); |
2563 sum += category_[i].EvictFreeListItemsInList(p); | 2434 sum += category->available(); |
2564 } | 2435 RemoveCategory(category); |
2565 } | 2436 category->Invalidate(); |
| 2437 }); |
2566 return sum; | 2438 return sum; |
2567 } | 2439 } |
2568 | 2440 |
| 2441 bool FreeList::ContainsPageFreeListItems(Page* page) { |
| 2442 bool contained = false; |
| 2443 page->ForAllFreeListCategories( |
| 2444 [this, &contained](FreeListCategory* category) { |
| 2445 if (category->owner() == this && category->is_linked()) { |
| 2446 contained = true; |
| 2447 } |
| 2448 }); |
| 2449 return contained; |
| 2450 } |
2569 | 2451 |
2570 bool FreeList::ContainsPageFreeListItems(Page* p) { | 2452 void FreeList::RepairLists(Heap* heap) { |
2571 for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | 2453 ForAllFreeListCategories( |
2572 if (category_[i].EvictFreeListItemsInList(p)) { | 2454 [heap](FreeListCategory* category) { category->RepairFreeList(heap); }); |
2573 return true; | 2455 } |
2574 } | 2456 |
| 2457 bool FreeList::AddCategory(FreeListCategory* category) { |
| 2458 FreeListCategoryType type = category->type_; |
| 2459 FreeListCategory* top = categories_[type]; |
| 2460 |
| 2461 if (category->is_empty()) return false; |
| 2462 if (top == category) return false; |
| 2463 |
| 2464 // Common double-linked list insertion. |
| 2465 if (top != nullptr) { |
| 2466 top->set_prev(category); |
2575 } | 2467 } |
2576 return false; | 2468 category->set_next(top); |
| 2469 categories_[type] = category; |
| 2470 return true; |
| 2471 } |
| 2472 |
| 2473 void FreeList::RemoveCategory(FreeListCategory* category) { |
| 2474 FreeListCategoryType type = category->type_; |
| 2475 FreeListCategory* top = categories_[type]; |
| 2476 |
| 2477 // Common double-linked list removal. |
| 2478 if (top == category) { |
| 2479 categories_[type] = category->next(); |
| 2480 } |
| 2481 if (category->prev() != nullptr) { |
| 2482 category->prev()->set_next(category->next()); |
| 2483 } |
| 2484 if (category->next() != nullptr) { |
| 2485 category->next()->set_prev(category->prev()); |
| 2486 } |
| 2487 category->set_next(nullptr); |
| 2488 category->set_prev(nullptr); |
| 2489 } |
| 2490 |
| 2491 void FreeList::PrintCategories(FreeListCategoryType type) { |
| 2492 FreeListCategoryIterator it(this, type); |
| 2493 PrintF("FreeList[%p, top=%p, %d] ", this, categories_[type], type); |
| 2494 while (it.HasNext()) { |
| 2495 FreeListCategory* current = it.Next(); |
| 2496 PrintF("%p -> ", current); |
| 2497 } |
| 2498 PrintF("null\n"); |
2577 } | 2499 } |
2578 | 2500 |
2579 | 2501 |
2580 void FreeList::RepairLists(Heap* heap) { | |
2581 for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | |
2582 category_[i].RepairFreeList(heap); | |
2583 } | |
2584 } | |
2585 | |
2586 | |
2587 #ifdef DEBUG | 2502 #ifdef DEBUG |
2588 intptr_t FreeListCategory::SumFreeList() { | 2503 intptr_t FreeListCategory::SumFreeList() { |
2589 intptr_t sum = 0; | 2504 intptr_t sum = 0; |
2590 FreeSpace* cur = top(); | 2505 FreeSpace* cur = top(); |
2591 while (cur != NULL) { | 2506 while (cur != NULL) { |
2592 DCHECK(cur->map() == cur->GetHeap()->root(Heap::kFreeSpaceMapRootIndex)); | 2507 DCHECK(cur->map() == cur->GetHeap()->root(Heap::kFreeSpaceMapRootIndex)); |
2593 sum += cur->nobarrier_size(); | 2508 sum += cur->nobarrier_size(); |
2594 cur = cur->next(); | 2509 cur = cur->next(); |
2595 } | 2510 } |
2596 return sum; | 2511 return sum; |
2597 } | 2512 } |
2598 | 2513 |
2599 | |
2600 int FreeListCategory::FreeListLength() { | 2514 int FreeListCategory::FreeListLength() { |
2601 int length = 0; | 2515 int length = 0; |
2602 FreeSpace* cur = top(); | 2516 FreeSpace* cur = top(); |
2603 while (cur != NULL) { | 2517 while (cur != NULL) { |
2604 length++; | 2518 length++; |
2605 cur = cur->next(); | 2519 cur = cur->next(); |
2606 if (length == kVeryLongFreeList) return length; | 2520 if (length == kVeryLongFreeList) return length; |
2607 } | 2521 } |
2608 return length; | 2522 return length; |
2609 } | 2523 } |
2610 | 2524 |
2611 | |
2612 bool FreeListCategory::IsVeryLong() { | |
2613 return FreeListLength() == kVeryLongFreeList; | |
2614 } | |
2615 | |
2616 | |
2617 bool FreeList::IsVeryLong() { | 2525 bool FreeList::IsVeryLong() { |
| 2526 int len = 0; |
2618 for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | 2527 for (int i = kFirstCategory; i < kNumberOfCategories; i++) { |
2619 if (category_[i].IsVeryLong()) { | 2528 FreeListCategoryIterator it(this, static_cast<FreeListCategoryType>(i)); |
2620 return true; | 2529 while (it.HasNext()) { |
| 2530 len += it.Next()->FreeListLength(); |
| 2531 if (len >= FreeListCategory::kVeryLongFreeList) return true; |
2621 } | 2532 } |
2622 } | 2533 } |
2623 return false; | 2534 return false; |
2624 } | 2535 } |
2625 | 2536 |
2626 | 2537 |
2627 // This can take a very long time because it is linear in the number of entries | 2538 // This can take a very long time because it is linear in the number of entries |
2628 // on the free list, so it should not be called if FreeListLength returns | 2539 // on the free list, so it should not be called if FreeListLength returns |
2629 // kVeryLongFreeList. | 2540 // kVeryLongFreeList. |
2630 intptr_t FreeList::SumFreeLists() { | 2541 intptr_t FreeList::SumFreeLists() { |
2631 intptr_t sum = 0; | 2542 intptr_t sum = 0; |
2632 for (int i = kFirstCategory; i < kNumberOfCategories; i++) { | 2543 ForAllFreeListCategories( |
2633 sum += category_[i].SumFreeList(); | 2544 [&sum](FreeListCategory* category) { sum += category->SumFreeList(); }); |
2634 } | |
2635 return sum; | 2545 return sum; |
2636 } | 2546 } |
2637 #endif | 2547 #endif |
2638 | 2548 |
2639 | 2549 |
2640 // ----------------------------------------------------------------------------- | 2550 // ----------------------------------------------------------------------------- |
2641 // OldSpace implementation | 2551 // OldSpace implementation |
2642 | 2552 |
2643 void PagedSpace::PrepareForMarkCompact() { | 2553 void PagedSpace::PrepareForMarkCompact() { |
2644 // We don't have a linear allocation area while sweeping. It will be restored | 2554 // We don't have a linear allocation area while sweeping. It will be restored |
(...skipping 597 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3242 object->ShortPrint(); | 3152 object->ShortPrint(); |
3243 PrintF("\n"); | 3153 PrintF("\n"); |
3244 } | 3154 } |
3245 printf(" --------------------------------------\n"); | 3155 printf(" --------------------------------------\n"); |
3246 printf(" Marked: %x, LiveCount: %x\n", mark_size, LiveBytes()); | 3156 printf(" Marked: %x, LiveCount: %x\n", mark_size, LiveBytes()); |
3247 } | 3157 } |
3248 | 3158 |
3249 #endif // DEBUG | 3159 #endif // DEBUG |
3250 } // namespace internal | 3160 } // namespace internal |
3251 } // namespace v8 | 3161 } // namespace v8 |
OLD | NEW |