Fri, 10 Oct 2014 15:51:58 +0200
8059758: Footprint regressions with JDK-8038423
Summary: Changes in JDK-8038423 always initialize (zero out) virtual memory used for auxiliary data structures. This causes a footprint regression for G1 in startup benchmarks. This is because they do not touch that memory at all, so the operating system does not actually commit these pages. The fix is to, if the initialization value of the data structures matches the default value of just committed memory (=0), do not do anything.
Reviewed-by: jwilhelm, brutisso
1 /*
2 * Copyright (c) 2001, 2013, 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 */
25 #include "precompiled.hpp"
26 #include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
27 #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp"
28 #include "gc_implementation/g1/heapRegion.hpp"
29 #include "gc_implementation/g1/satbQueue.hpp"
30 #include "runtime/mutexLocker.hpp"
31 #include "runtime/orderAccess.inline.hpp"
32 #include "runtime/thread.inline.hpp"
34 G1SATBCardTableModRefBS::G1SATBCardTableModRefBS(MemRegion whole_heap,
35 int max_covered_regions) :
36 CardTableModRefBSForCTRS(whole_heap, max_covered_regions)
37 {
38 _kind = G1SATBCT;
39 }
41 void G1SATBCardTableModRefBS::enqueue(oop pre_val) {
42 // Nulls should have been already filtered.
43 assert(pre_val->is_oop(true), "Error");
45 if (!JavaThread::satb_mark_queue_set().is_active()) return;
46 Thread* thr = Thread::current();
47 if (thr->is_Java_thread()) {
48 JavaThread* jt = (JavaThread*)thr;
49 jt->satb_mark_queue().enqueue(pre_val);
50 } else {
51 MutexLockerEx x(Shared_SATB_Q_lock, Mutex::_no_safepoint_check_flag);
52 JavaThread::satb_mark_queue_set().shared_satb_queue()->enqueue(pre_val);
53 }
54 }
56 template <class T> void
57 G1SATBCardTableModRefBS::write_ref_array_pre_work(T* dst, int count) {
58 if (!JavaThread::satb_mark_queue_set().is_active()) return;
59 T* elem_ptr = dst;
60 for (int i = 0; i < count; i++, elem_ptr++) {
61 T heap_oop = oopDesc::load_heap_oop(elem_ptr);
62 if (!oopDesc::is_null(heap_oop)) {
63 enqueue(oopDesc::decode_heap_oop_not_null(heap_oop));
64 }
65 }
66 }
68 void G1SATBCardTableModRefBS::write_ref_array_pre(oop* dst, int count, bool dest_uninitialized) {
69 if (!dest_uninitialized) {
70 write_ref_array_pre_work(dst, count);
71 }
72 }
73 void G1SATBCardTableModRefBS::write_ref_array_pre(narrowOop* dst, int count, bool dest_uninitialized) {
74 if (!dest_uninitialized) {
75 write_ref_array_pre_work(dst, count);
76 }
77 }
79 bool G1SATBCardTableModRefBS::mark_card_deferred(size_t card_index) {
80 jbyte val = _byte_map[card_index];
81 // It's already processed
82 if ((val & (clean_card_mask_val() | deferred_card_val())) == deferred_card_val()) {
83 return false;
84 }
86 if (val == g1_young_gen) {
87 // the card is for a young gen region. We don't need to keep track of all pointers into young
88 return false;
89 }
91 // Cached bit can be installed either on a clean card or on a claimed card.
92 jbyte new_val = val;
93 if (val == clean_card_val()) {
94 new_val = (jbyte)deferred_card_val();
95 } else {
96 if (val & claimed_card_val()) {
97 new_val = val | (jbyte)deferred_card_val();
98 }
99 }
100 if (new_val != val) {
101 Atomic::cmpxchg(new_val, &_byte_map[card_index], val);
102 }
103 return true;
104 }
106 void G1SATBCardTableModRefBS::g1_mark_as_young(const MemRegion& mr) {
107 jbyte *const first = byte_for(mr.start());
108 jbyte *const last = byte_after(mr.last());
110 // Below we may use an explicit loop instead of memset() because on
111 // certain platforms memset() can give concurrent readers phantom zeros.
112 if (UseMemSetInBOT) {
113 memset(first, g1_young_gen, last - first);
114 } else {
115 for (jbyte* i = first; i < last; i++) {
116 *i = g1_young_gen;
117 }
118 }
119 }
121 #ifndef PRODUCT
122 void G1SATBCardTableModRefBS::verify_g1_young_region(MemRegion mr) {
123 verify_region(mr, g1_young_gen, true);
124 }
125 #endif
127 void G1SATBCardTableLoggingModRefBSChangedListener::on_commit(uint start_idx, size_t num_regions, bool zero_filled) {
128 // Default value for a clean card on the card table is -1. So we cannot take advantage of the zero_filled parameter.
129 MemRegion mr(G1CollectedHeap::heap()->bottom_addr_for_region(start_idx), num_regions * HeapRegion::GrainWords);
130 _card_table->clear(mr);
131 }
133 G1SATBCardTableLoggingModRefBS::
134 G1SATBCardTableLoggingModRefBS(MemRegion whole_heap,
135 int max_covered_regions) :
136 G1SATBCardTableModRefBS(whole_heap, max_covered_regions),
137 _dcqs(JavaThread::dirty_card_queue_set()),
138 _listener()
139 {
140 _kind = G1SATBCTLogging;
141 _listener.set_card_table(this);
142 }
144 void G1SATBCardTableLoggingModRefBS::initialize(G1RegionToSpaceMapper* mapper) {
145 mapper->set_mapping_changed_listener(&_listener);
147 _byte_map_size = mapper->reserved().byte_size();
149 _guard_index = cards_required(_whole_heap.word_size()) - 1;
150 _last_valid_index = _guard_index - 1;
152 HeapWord* low_bound = _whole_heap.start();
153 HeapWord* high_bound = _whole_heap.end();
155 _cur_covered_regions = 1;
156 _covered[0] = _whole_heap;
158 _byte_map = (jbyte*) mapper->reserved().start();
159 byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift);
160 assert(byte_for(low_bound) == &_byte_map[0], "Checking start of map");
161 assert(byte_for(high_bound-1) <= &_byte_map[_last_valid_index], "Checking end of map");
163 if (TraceCardTableModRefBS) {
164 gclog_or_tty->print_cr("G1SATBCardTableModRefBS::G1SATBCardTableModRefBS: ");
165 gclog_or_tty->print_cr(" "
166 " &_byte_map[0]: " INTPTR_FORMAT
167 " &_byte_map[_last_valid_index]: " INTPTR_FORMAT,
168 p2i(&_byte_map[0]),
169 p2i(&_byte_map[_last_valid_index]));
170 gclog_or_tty->print_cr(" "
171 " byte_map_base: " INTPTR_FORMAT,
172 p2i(byte_map_base));
173 }
174 }
176 void
177 G1SATBCardTableLoggingModRefBS::write_ref_field_work(void* field,
178 oop new_val,
179 bool release) {
180 volatile jbyte* byte = byte_for(field);
181 if (*byte == g1_young_gen) {
182 return;
183 }
184 OrderAccess::storeload();
185 if (*byte != dirty_card) {
186 *byte = dirty_card;
187 Thread* thr = Thread::current();
188 if (thr->is_Java_thread()) {
189 JavaThread* jt = (JavaThread*)thr;
190 jt->dirty_card_queue().enqueue(byte);
191 } else {
192 MutexLockerEx x(Shared_DirtyCardQ_lock,
193 Mutex::_no_safepoint_check_flag);
194 _dcqs.shared_dirty_card_queue()->enqueue(byte);
195 }
196 }
197 }
199 void
200 G1SATBCardTableLoggingModRefBS::write_ref_field_static(void* field,
201 oop new_val) {
202 uintptr_t field_uint = (uintptr_t)field;
203 uintptr_t new_val_uint = cast_from_oop<uintptr_t>(new_val);
204 uintptr_t comb = field_uint ^ new_val_uint;
205 comb = comb >> HeapRegion::LogOfHRGrainBytes;
206 if (comb == 0) return;
207 if (new_val == NULL) return;
208 // Otherwise, log it.
209 G1SATBCardTableLoggingModRefBS* g1_bs =
210 (G1SATBCardTableLoggingModRefBS*)Universe::heap()->barrier_set();
211 g1_bs->write_ref_field_work(field, new_val);
212 }
214 void
215 G1SATBCardTableLoggingModRefBS::invalidate(MemRegion mr, bool whole_heap) {
216 volatile jbyte* byte = byte_for(mr.start());
217 jbyte* last_byte = byte_for(mr.last());
218 Thread* thr = Thread::current();
219 if (whole_heap) {
220 while (byte <= last_byte) {
221 *byte = dirty_card;
222 byte++;
223 }
224 } else {
225 // skip all consecutive young cards
226 for (; byte <= last_byte && *byte == g1_young_gen; byte++);
228 if (byte <= last_byte) {
229 OrderAccess::storeload();
230 // Enqueue if necessary.
231 if (thr->is_Java_thread()) {
232 JavaThread* jt = (JavaThread*)thr;
233 for (; byte <= last_byte; byte++) {
234 if (*byte == g1_young_gen) {
235 continue;
236 }
237 if (*byte != dirty_card) {
238 *byte = dirty_card;
239 jt->dirty_card_queue().enqueue(byte);
240 }
241 }
242 } else {
243 MutexLockerEx x(Shared_DirtyCardQ_lock,
244 Mutex::_no_safepoint_check_flag);
245 for (; byte <= last_byte; byte++) {
246 if (*byte == g1_young_gen) {
247 continue;
248 }
249 if (*byte != dirty_card) {
250 *byte = dirty_card;
251 _dcqs.shared_dirty_card_queue()->enqueue(byte);
252 }
253 }
254 }
255 }
256 }
257 }