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