Mercurial > hg > truffle
annotate src/share/vm/memory/threadLocalAllocBuffer.cpp @ 20543:e7d0505c8a30
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
author | tschatzl |
---|---|
date | Fri, 10 Oct 2014 15:51:58 +0200 |
parents | 78bbf4d43a14 |
children | 52b4284cb496 060cdf93040c |
rev | line source |
---|---|
0 | 1 /* |
17937
78bbf4d43a14
8037816: Fix for 8036122 breaks build with Xcode5/clang
drchase
parents:
17727
diff
changeset
|
2 * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. |
0 | 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 * | |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1165
diff
changeset
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1165
diff
changeset
|
20 * or visit www.oracle.com if you need additional information or have any |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1165
diff
changeset
|
21 * questions. |
0 | 22 * |
23 */ | |
24 | |
1972 | 25 #include "precompiled.hpp" |
26 #include "memory/genCollectedHeap.hpp" | |
27 #include "memory/resourceArea.hpp" | |
28 #include "memory/threadLocalAllocBuffer.inline.hpp" | |
29 #include "memory/universe.inline.hpp" | |
30 #include "oops/oop.inline.hpp" | |
7180
f34d701e952e
8003935: Simplify the needed includes for using Thread::current()
stefank
parents:
3960
diff
changeset
|
31 #include "runtime/thread.inline.hpp" |
1972 | 32 #include "utilities/copy.hpp" |
0 | 33 |
17937
78bbf4d43a14
8037816: Fix for 8036122 breaks build with Xcode5/clang
drchase
parents:
17727
diff
changeset
|
34 PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC |
78bbf4d43a14
8037816: Fix for 8036122 breaks build with Xcode5/clang
drchase
parents:
17727
diff
changeset
|
35 |
1972 | 36 // Thread-Local Edens support |
0 | 37 |
38 // static member initialization | |
17727 | 39 size_t ThreadLocalAllocBuffer::_max_size = 0; |
0 | 40 unsigned ThreadLocalAllocBuffer::_target_refills = 0; |
41 GlobalTLABStats* ThreadLocalAllocBuffer::_global_stats = NULL; | |
42 | |
43 void ThreadLocalAllocBuffer::clear_before_allocation() { | |
44 _slow_refill_waste += (unsigned)remaining(); | |
45 make_parsable(true); // also retire the TLAB | |
46 } | |
47 | |
48 void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() { | |
49 global_stats()->initialize(); | |
50 | |
17727 | 51 for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { |
0 | 52 thread->tlab().accumulate_statistics(); |
53 thread->tlab().initialize_statistics(); | |
54 } | |
55 | |
56 // Publish new stats if some allocation occurred. | |
57 if (global_stats()->allocation() != 0) { | |
58 global_stats()->publish(); | |
59 if (PrintTLAB) { | |
60 global_stats()->print(); | |
61 } | |
62 } | |
63 } | |
64 | |
65 void ThreadLocalAllocBuffer::accumulate_statistics() { | |
17727 | 66 Thread* thread = myThread(); |
67 size_t capacity = Universe::heap()->tlab_capacity(thread); | |
68 size_t used = Universe::heap()->tlab_used(thread); | |
0 | 69 |
70 _gc_waste += (unsigned)remaining(); | |
17727 | 71 size_t total_allocated = thread->allocated_bytes(); |
72 size_t allocated_since_last_gc = total_allocated - _allocated_before_last_gc; | |
73 _allocated_before_last_gc = total_allocated; | |
0 | 74 |
75 if (PrintTLAB && (_number_of_refills > 0 || Verbose)) { | |
76 print_stats("gc"); | |
77 } | |
78 | |
79 if (_number_of_refills > 0) { | |
17727 | 80 // Update allocation history if a reasonable amount of eden was allocated. |
81 bool update_allocation_history = used > 0.5 * capacity; | |
0 | 82 |
83 if (update_allocation_history) { | |
84 // Average the fraction of eden allocated in a tlab by this | |
85 // thread for use in the next resize operation. | |
86 // _gc_waste is not subtracted because it's included in | |
87 // "used". | |
17727 | 88 // The result can be larger than 1.0 due to direct to old allocations. |
89 // These allocations should ideally not be counted but since it is not possible | |
90 // to filter them out here we just cap the fraction to be at most 1.0. | |
91 double alloc_frac = MIN2(1.0, (double) allocated_since_last_gc / used); | |
0 | 92 _allocation_fraction.sample(alloc_frac); |
93 } | |
94 global_stats()->update_allocating_threads(); | |
95 global_stats()->update_number_of_refills(_number_of_refills); | |
96 global_stats()->update_allocation(_number_of_refills * desired_size()); | |
97 global_stats()->update_gc_waste(_gc_waste); | |
98 global_stats()->update_slow_refill_waste(_slow_refill_waste); | |
99 global_stats()->update_fast_refill_waste(_fast_refill_waste); | |
100 | |
101 } else { | |
102 assert(_number_of_refills == 0 && _fast_refill_waste == 0 && | |
103 _slow_refill_waste == 0 && _gc_waste == 0, | |
104 "tlab stats == 0"); | |
105 } | |
106 global_stats()->update_slow_allocations(_slow_allocations); | |
107 } | |
108 | |
109 // Fills the current tlab with a dummy filler array to create | |
110 // an illusion of a contiguous Eden and optionally retires the tlab. | |
111 // Waste accounting should be done in caller as appropriate; see, | |
112 // for example, clear_before_allocation(). | |
113 void ThreadLocalAllocBuffer::make_parsable(bool retire) { | |
114 if (end() != NULL) { | |
115 invariants(); | |
2100
b1a2afa37ec4
7003271: Hotspot should track cumulative Java heap bytes allocated on a per-thread basis
phh
parents:
1972
diff
changeset
|
116 |
b1a2afa37ec4
7003271: Hotspot should track cumulative Java heap bytes allocated on a per-thread basis
phh
parents:
1972
diff
changeset
|
117 if (retire) { |
b1a2afa37ec4
7003271: Hotspot should track cumulative Java heap bytes allocated on a per-thread basis
phh
parents:
1972
diff
changeset
|
118 myThread()->incr_allocated_bytes(used_bytes()); |
b1a2afa37ec4
7003271: Hotspot should track cumulative Java heap bytes allocated on a per-thread basis
phh
parents:
1972
diff
changeset
|
119 } |
b1a2afa37ec4
7003271: Hotspot should track cumulative Java heap bytes allocated on a per-thread basis
phh
parents:
1972
diff
changeset
|
120 |
1165
2dd52dea6d28
6902115: G1:assert(ignore_max_completed||thread->is_Java_thread()||SafepointSynchronize::is_at_safepoint())
johnc
parents:
579
diff
changeset
|
121 CollectedHeap::fill_with_object(top(), hard_end(), retire); |
0 | 122 |
123 if (retire || ZeroTLAB) { // "Reset" the TLAB | |
124 set_start(NULL); | |
125 set_top(NULL); | |
126 set_pf_top(NULL); | |
127 set_end(NULL); | |
128 } | |
129 } | |
130 assert(!(retire || ZeroTLAB) || | |
131 (start() == NULL && end() == NULL && top() == NULL), | |
132 "TLAB must be reset"); | |
133 } | |
134 | |
135 void ThreadLocalAllocBuffer::resize_all_tlabs() { | |
17727 | 136 if (ResizeTLAB) { |
137 for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) { | |
138 thread->tlab().resize(); | |
139 } | |
0 | 140 } |
141 } | |
142 | |
143 void ThreadLocalAllocBuffer::resize() { | |
17727 | 144 // Compute the next tlab size using expected allocation amount |
145 assert(ResizeTLAB, "Should not call this otherwise"); | |
146 size_t alloc = (size_t)(_allocation_fraction.average() * | |
147 (Universe::heap()->tlab_capacity(myThread()) / HeapWordSize)); | |
148 size_t new_size = alloc / _target_refills; | |
0 | 149 |
17727 | 150 new_size = MIN2(MAX2(new_size, min_size()), max_size()); |
0 | 151 |
17727 | 152 size_t aligned_new_size = align_object_size(new_size); |
0 | 153 |
17727 | 154 if (PrintTLAB && Verbose) { |
155 gclog_or_tty->print("TLAB new size: thread: " INTPTR_FORMAT " [id: %2d]" | |
156 " refills %d alloc: %8.6f desired_size: " SIZE_FORMAT " -> " SIZE_FORMAT "\n", | |
157 myThread(), myThread()->osthread()->thread_id(), | |
158 _target_refills, _allocation_fraction.average(), desired_size(), aligned_new_size); | |
0 | 159 } |
17727 | 160 set_desired_size(aligned_new_size); |
161 set_refill_waste_limit(initial_refill_waste_limit()); | |
0 | 162 } |
163 | |
164 void ThreadLocalAllocBuffer::initialize_statistics() { | |
165 _number_of_refills = 0; | |
166 _fast_refill_waste = 0; | |
167 _slow_refill_waste = 0; | |
168 _gc_waste = 0; | |
169 _slow_allocations = 0; | |
170 } | |
171 | |
172 void ThreadLocalAllocBuffer::fill(HeapWord* start, | |
173 HeapWord* top, | |
174 size_t new_size) { | |
175 _number_of_refills++; | |
176 if (PrintTLAB && Verbose) { | |
177 print_stats("fill"); | |
178 } | |
179 assert(top <= start + new_size - alignment_reserve(), "size too small"); | |
180 initialize(start, top, start + new_size - alignment_reserve()); | |
181 | |
182 // Reset amount of internal fragmentation | |
183 set_refill_waste_limit(initial_refill_waste_limit()); | |
184 } | |
185 | |
186 void ThreadLocalAllocBuffer::initialize(HeapWord* start, | |
187 HeapWord* top, | |
188 HeapWord* end) { | |
189 set_start(start); | |
190 set_top(top); | |
191 set_pf_top(top); | |
192 set_end(end); | |
193 invariants(); | |
194 } | |
195 | |
196 void ThreadLocalAllocBuffer::initialize() { | |
197 initialize(NULL, // start | |
198 NULL, // top | |
199 NULL); // end | |
200 | |
201 set_desired_size(initial_desired_size()); | |
202 | |
203 // Following check is needed because at startup the main (primordial) | |
204 // thread is initialized before the heap is. The initialization for | |
205 // this thread is redone in startup_initialization below. | |
206 if (Universe::heap() != NULL) { | |
207 size_t capacity = Universe::heap()->tlab_capacity(myThread()) / HeapWordSize; | |
208 double alloc_frac = desired_size() * target_refills() / (double) capacity; | |
209 _allocation_fraction.sample(alloc_frac); | |
210 } | |
211 | |
212 set_refill_waste_limit(initial_refill_waste_limit()); | |
213 | |
214 initialize_statistics(); | |
215 } | |
216 | |
217 void ThreadLocalAllocBuffer::startup_initialization() { | |
218 | |
219 // Assuming each thread's active tlab is, on average, | |
220 // 1/2 full at a GC | |
221 _target_refills = 100 / (2 * TLABWasteTargetPercent); | |
222 _target_refills = MAX2(_target_refills, (unsigned)1U); | |
223 | |
224 _global_stats = new GlobalTLABStats(); | |
225 | |
226 // During jvm startup, the main (primordial) thread is initialized | |
227 // before the heap is initialized. So reinitialize it now. | |
228 guarantee(Thread::current()->is_Java_thread(), "tlab initialization thread not Java thread"); | |
229 Thread::current()->tlab().initialize(); | |
230 | |
231 if (PrintTLAB && Verbose) { | |
232 gclog_or_tty->print("TLAB min: " SIZE_FORMAT " initial: " SIZE_FORMAT " max: " SIZE_FORMAT "\n", | |
233 min_size(), Thread::current()->tlab().initial_desired_size(), max_size()); | |
234 } | |
235 } | |
236 | |
237 size_t ThreadLocalAllocBuffer::initial_desired_size() { | |
238 size_t init_sz; | |
239 | |
240 if (TLABSize > 0) { | |
241 init_sz = MIN2(TLABSize / HeapWordSize, max_size()); | |
242 } else if (global_stats() == NULL) { | |
243 // Startup issue - main thread initialized before heap initialized. | |
244 init_sz = min_size(); | |
245 } else { | |
246 // Initial size is a function of the average number of allocating threads. | |
247 unsigned nof_threads = global_stats()->allocating_threads_avg(); | |
248 | |
249 init_sz = (Universe::heap()->tlab_capacity(myThread()) / HeapWordSize) / | |
250 (nof_threads * target_refills()); | |
251 init_sz = align_object_size(init_sz); | |
252 init_sz = MIN2(MAX2(init_sz, min_size()), max_size()); | |
253 } | |
254 return init_sz; | |
255 } | |
256 | |
257 void ThreadLocalAllocBuffer::print_stats(const char* tag) { | |
258 Thread* thrd = myThread(); | |
259 size_t waste = _gc_waste + _slow_refill_waste + _fast_refill_waste; | |
260 size_t alloc = _number_of_refills * _desired_size; | |
261 double waste_percent = alloc == 0 ? 0.0 : | |
262 100.0 * waste / alloc; | |
17727 | 263 size_t tlab_used = Universe::heap()->tlab_used(thrd); |
0 | 264 gclog_or_tty->print("TLAB: %s thread: " INTPTR_FORMAT " [id: %2d]" |
265 " desired_size: " SIZE_FORMAT "KB" | |
266 " slow allocs: %d refill waste: " SIZE_FORMAT "B" | |
267 " alloc:%8.5f %8.0fKB refills: %d waste %4.1f%% gc: %dB" | |
268 " slow: %dB fast: %dB\n", | |
269 tag, thrd, thrd->osthread()->thread_id(), | |
270 _desired_size / (K / HeapWordSize), | |
271 _slow_allocations, _refill_waste_limit * HeapWordSize, | |
272 _allocation_fraction.average(), | |
273 _allocation_fraction.average() * tlab_used / K, | |
274 _number_of_refills, waste_percent, | |
275 _gc_waste * HeapWordSize, | |
276 _slow_refill_waste * HeapWordSize, | |
277 _fast_refill_waste * HeapWordSize); | |
278 } | |
279 | |
280 void ThreadLocalAllocBuffer::verify() { | |
281 HeapWord* p = start(); | |
282 HeapWord* t = top(); | |
283 HeapWord* prev_p = NULL; | |
284 while (p < t) { | |
285 oop(p)->verify(); | |
286 prev_p = p; | |
287 p += oop(p)->size(); | |
288 } | |
289 guarantee(p == top(), "end of last object must match end of space"); | |
290 } | |
291 | |
292 Thread* ThreadLocalAllocBuffer::myThread() { | |
293 return (Thread*)(((char *)this) + | |
294 in_bytes(start_offset()) - | |
295 in_bytes(Thread::tlab_start_offset())); | |
296 } | |
297 | |
298 | |
299 GlobalTLABStats::GlobalTLABStats() : | |
300 _allocating_threads_avg(TLABAllocationWeight) { | |
301 | |
302 initialize(); | |
303 | |
304 _allocating_threads_avg.sample(1); // One allocating thread at startup | |
305 | |
306 if (UsePerfData) { | |
307 | |
308 EXCEPTION_MARK; | |
309 ResourceMark rm; | |
310 | |
311 char* cname = PerfDataManager::counter_name("tlab", "allocThreads"); | |
312 _perf_allocating_threads = | |
313 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, CHECK); | |
314 | |
315 cname = PerfDataManager::counter_name("tlab", "fills"); | |
316 _perf_total_refills = | |
317 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, CHECK); | |
318 | |
319 cname = PerfDataManager::counter_name("tlab", "maxFills"); | |
320 _perf_max_refills = | |
321 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, CHECK); | |
322 | |
323 cname = PerfDataManager::counter_name("tlab", "alloc"); | |
324 _perf_allocation = | |
325 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); | |
326 | |
327 cname = PerfDataManager::counter_name("tlab", "gcWaste"); | |
328 _perf_gc_waste = | |
329 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); | |
330 | |
331 cname = PerfDataManager::counter_name("tlab", "maxGcWaste"); | |
332 _perf_max_gc_waste = | |
333 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); | |
334 | |
335 cname = PerfDataManager::counter_name("tlab", "slowWaste"); | |
336 _perf_slow_refill_waste = | |
337 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); | |
338 | |
339 cname = PerfDataManager::counter_name("tlab", "maxSlowWaste"); | |
340 _perf_max_slow_refill_waste = | |
341 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); | |
342 | |
343 cname = PerfDataManager::counter_name("tlab", "fastWaste"); | |
344 _perf_fast_refill_waste = | |
345 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); | |
346 | |
347 cname = PerfDataManager::counter_name("tlab", "maxFastWaste"); | |
348 _perf_max_fast_refill_waste = | |
349 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes, CHECK); | |
350 | |
351 cname = PerfDataManager::counter_name("tlab", "slowAlloc"); | |
352 _perf_slow_allocations = | |
353 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, CHECK); | |
354 | |
355 cname = PerfDataManager::counter_name("tlab", "maxSlowAlloc"); | |
356 _perf_max_slow_allocations = | |
357 PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_None, CHECK); | |
358 } | |
359 } | |
360 | |
361 void GlobalTLABStats::initialize() { | |
362 // Clear counters summarizing info from all threads | |
363 _allocating_threads = 0; | |
364 _total_refills = 0; | |
365 _max_refills = 0; | |
366 _total_allocation = 0; | |
367 _total_gc_waste = 0; | |
368 _max_gc_waste = 0; | |
369 _total_slow_refill_waste = 0; | |
370 _max_slow_refill_waste = 0; | |
371 _total_fast_refill_waste = 0; | |
372 _max_fast_refill_waste = 0; | |
373 _total_slow_allocations = 0; | |
374 _max_slow_allocations = 0; | |
375 } | |
376 | |
377 void GlobalTLABStats::publish() { | |
378 _allocating_threads_avg.sample(_allocating_threads); | |
379 if (UsePerfData) { | |
380 _perf_allocating_threads ->set_value(_allocating_threads); | |
381 _perf_total_refills ->set_value(_total_refills); | |
382 _perf_max_refills ->set_value(_max_refills); | |
383 _perf_allocation ->set_value(_total_allocation); | |
384 _perf_gc_waste ->set_value(_total_gc_waste); | |
385 _perf_max_gc_waste ->set_value(_max_gc_waste); | |
386 _perf_slow_refill_waste ->set_value(_total_slow_refill_waste); | |
387 _perf_max_slow_refill_waste->set_value(_max_slow_refill_waste); | |
388 _perf_fast_refill_waste ->set_value(_total_fast_refill_waste); | |
389 _perf_max_fast_refill_waste->set_value(_max_fast_refill_waste); | |
390 _perf_slow_allocations ->set_value(_total_slow_allocations); | |
391 _perf_max_slow_allocations ->set_value(_max_slow_allocations); | |
392 } | |
393 } | |
394 | |
395 void GlobalTLABStats::print() { | |
396 size_t waste = _total_gc_waste + _total_slow_refill_waste + _total_fast_refill_waste; | |
397 double waste_percent = _total_allocation == 0 ? 0.0 : | |
398 100.0 * waste / _total_allocation; | |
399 gclog_or_tty->print("TLAB totals: thrds: %d refills: %d max: %d" | |
400 " slow allocs: %d max %d waste: %4.1f%%" | |
401 " gc: " SIZE_FORMAT "B max: " SIZE_FORMAT "B" | |
402 " slow: " SIZE_FORMAT "B max: " SIZE_FORMAT "B" | |
403 " fast: " SIZE_FORMAT "B max: " SIZE_FORMAT "B\n", | |
404 _allocating_threads, | |
405 _total_refills, _max_refills, | |
406 _total_slow_allocations, _max_slow_allocations, | |
407 waste_percent, | |
408 _total_gc_waste * HeapWordSize, | |
409 _max_gc_waste * HeapWordSize, | |
410 _total_slow_refill_waste * HeapWordSize, | |
411 _max_slow_refill_waste * HeapWordSize, | |
412 _total_fast_refill_waste * HeapWordSize, | |
413 _max_fast_refill_waste * HeapWordSize); | |
414 } |