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
ZPageCache()37 ZPageCache::ZPageCache() :
38 _available(0),
39 _small(),
40 _medium(),
41 _large() {}
42
alloc_small_page()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
alloc_medium_page()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
alloc_large_page(size_t size)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
alloc_page(uint8_t type,size_t size)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
free_page(ZPage * page)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
flush_list(ZList<ZPage> * from,size_t requested,ZList<ZPage> * to,size_t * flushed)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
flush_per_numa_lists(ZPerNUMA<ZList<ZPage>> * from,size_t requested,ZList<ZPage> * to,size_t * flushed)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
flush(ZList<ZPage> * to,size_t requested)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 }
198