1 /* 2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 #include "precompiled.hpp" 25 #include "gc/z/zList.inline.hpp" 26 #include "gc/z/zNUMA.hpp" 27 #include "gc/z/zPage.inline.hpp" 28 #include "gc/z/zPageCache.hpp" 29 #include "gc/z/zStat.hpp" 30 #include "logging/log.hpp" 31 32 static const ZStatCounter ZCounterPageCacheHitL1("Memory", "Page Cache Hit L1", ZStatUnitOpsPerSecond); 33 static const ZStatCounter ZCounterPageCacheHitL2("Memory", "Page Cache Hit L2", ZStatUnitOpsPerSecond); 34 static const ZStatCounter ZCounterPageCacheMiss("Memory", "Page Cache Miss", ZStatUnitOpsPerSecond); 35 static const ZStatCounter ZCounterPageCacheFlush("Memory", "Page Cache Flush", ZStatUnitBytesPerSecond); 36 37 ZPageCache::ZPageCache() : 38 _available(0), 39 _small(), 40 _medium(), 41 _large() {} 42 43 ZPage* ZPageCache::alloc_small_page() { 44 const uint32_t numa_id = ZNUMA::id(); 45 const uint32_t numa_count = ZNUMA::count(); 46 47 // Try NUMA local page cache 48 ZPage* const l1_page = _small.get(numa_id).remove_first(); 49 if (l1_page != NULL) { 50 ZStatInc(ZCounterPageCacheHitL1); 51 return l1_page; 52 } 53 54 // Try NUMA remote page cache(s) 55 uint32_t remote_numa_id = numa_id + 1; 56 const uint32_t remote_numa_count = numa_count - 1; 57 for (uint32_t i = 0; i < remote_numa_count; i++) { 58 if (remote_numa_id == numa_count) { 59 remote_numa_id = 0; 60 } 61 62 ZPage* const l2_page = _small.get(remote_numa_id).remove_first(); 63 if (l2_page != NULL) { 64 ZStatInc(ZCounterPageCacheHitL2); 65 return l2_page; 66 } 67 68 remote_numa_id++; 69 } 70 71 ZStatInc(ZCounterPageCacheMiss); 72 return NULL; 73 } 74 75 ZPage* ZPageCache::alloc_medium_page() { 76 ZPage* const l1_page = _medium.remove_first(); 77 if (l1_page != NULL) { 78 ZStatInc(ZCounterPageCacheHitL1); 79 return l1_page; 80 } 81 82 ZStatInc(ZCounterPageCacheMiss); 83 return NULL; 84 } 85 86 ZPage* ZPageCache::alloc_large_page(size_t size) { 87 // Find a page with the right size 88 ZListIterator<ZPage> iter(&_large); 89 for (ZPage* l1_page; iter.next(&l1_page);) { 90 if (l1_page->size() == size) { 91 // Page found 92 _large.remove(l1_page); 93 ZStatInc(ZCounterPageCacheHitL1); 94 return l1_page; 95 } 96 } 97 98 ZStatInc(ZCounterPageCacheMiss); 99 return NULL; 100 } 101 102 ZPage* ZPageCache::alloc_page(uint8_t type, size_t size) { 103 ZPage* page; 104 105 if (type == ZPageTypeSmall) { 106 page = alloc_small_page(); 107 } else if (type == ZPageTypeMedium) { 108 page = alloc_medium_page(); 109 } else { 110 page = alloc_large_page(size); 111 } 112 113 if (page != NULL) { 114 _available -= page->size(); 115 } 116 117 return page; 118 } 119 120 void ZPageCache::free_page(ZPage* page) { 121 assert(!page->is_active(), "Invalid page state"); 122 assert(!page->is_pinned(), "Invalid page state"); 123 assert(!page->is_detached(), "Invalid page state"); 124 125 const uint8_t type = page->type(); 126 if (type == ZPageTypeSmall) { 127 _small.get(page->numa_id()).insert_first(page); 128 } else if (type == ZPageTypeMedium) { 129 _medium.insert_first(page); 130 } else { 131 _large.insert_first(page); 132 } 133 134 _available += page->size(); 135 } 136 137 void ZPageCache::flush_list(ZList<ZPage>* from, size_t requested, ZList<ZPage>* to, size_t* flushed) { 138 while (*flushed < requested) { 139 // Flush least recently used 140 ZPage* const page = from->remove_last(); 141 if (page == NULL) { 142 break; 143 } 144 145 *flushed += page->size(); 146 to->insert_last(page); 147 } 148 } 149 150 void ZPageCache::flush_per_numa_lists(ZPerNUMA<ZList<ZPage> >* from, size_t requested, ZList<ZPage>* to, size_t* flushed) { 151 const uint32_t numa_count = ZNUMA::count(); 152 uint32_t numa_empty = 0; 153 uint32_t numa_next = 0; 154 155 // Flush lists round-robin 156 while (*flushed < requested) { 157 ZPage* const page = from->get(numa_next).remove_last(); 158 159 if (++numa_next == numa_count) { 160 numa_next = 0; 161 } 162 163 if (page == NULL) { 164 // List is empty 165 if (++numa_empty == numa_count) { 166 // All lists are empty 167 break; 168 } 169 170 // Try next list 171 continue; 172 } 173 174 // Flush page 175 numa_empty = 0; 176 *flushed += page->size(); 177 to->insert_last(page); 178 } 179 } 180 181 void ZPageCache::flush(ZList<ZPage>* to, size_t requested) { 182 size_t flushed = 0; 183 184 // Prefer flushing large, then medium and last small pages 185 flush_list(&_large, requested, to, &flushed); 186 flush_list(&_medium, requested, to, &flushed); 187 flush_per_numa_lists(&_small, requested, to, &flushed); 188 189 ZStatInc(ZCounterPageCacheFlush, flushed); 190 191 log_info(gc, heap)("Page Cache Flushed: " 192 SIZE_FORMAT "M requested, " 193 SIZE_FORMAT "M(" SIZE_FORMAT "M->" SIZE_FORMAT "M) flushed", 194 requested / M, flushed / M , _available / M, (_available - flushed) / M); 195 196 _available -= flushed; 197 }