174
175 size_t ShenandoahPacer::update_and_get_progress_history() {
176 if (_progress == -1) {
177 // First initialization, report some prior
178 Atomic::store(&_progress, (intptr_t)PACING_PROGRESS_ZERO);
179 return (size_t) (_heap->max_capacity() * 0.1);
180 } else {
181 // Record history, and reply historical data
182 _progress_history->add(_progress);
183 Atomic::store(&_progress, (intptr_t)PACING_PROGRESS_ZERO);
184 return (size_t) (_progress_history->avg() * HeapWordSize);
185 }
186 }
187
188 void ShenandoahPacer::restart_with(size_t non_taxable_bytes, double tax_rate) {
189 size_t initial = (size_t)(non_taxable_bytes * tax_rate) >> LogHeapWordSize;
190 STATIC_ASSERT(sizeof(size_t) <= sizeof(intptr_t));
191 Atomic::xchg(&_budget, (intptr_t)initial);
192 Atomic::store(&_tax_rate, tax_rate);
193 Atomic::inc(&_epoch);
194 }
195
196 bool ShenandoahPacer::claim_for_alloc(size_t words, bool force) {
197 assert(ShenandoahPacing, "Only be here when pacing is enabled");
198
199 intptr_t tax = MAX2<intptr_t>(1, words * Atomic::load(&_tax_rate));
200
201 intptr_t cur = 0;
202 intptr_t new_val = 0;
203 do {
204 cur = Atomic::load(&_budget);
205 if (cur < tax && !force) {
206 // Progress depleted, alas.
207 return false;
208 }
209 new_val = cur - tax;
210 } while (Atomic::cmpxchg(&_budget, cur, new_val) != cur);
211 return true;
212 }
213
214 void ShenandoahPacer::unpace_for_alloc(intptr_t epoch, size_t words) {
215 assert(ShenandoahPacing, "Only be here when pacing is enabled");
216
217 if (_epoch != epoch) {
218 // Stale ticket, no need to unpace.
219 return;
220 }
221
222 intptr_t tax = MAX2<intptr_t>(1, words * Atomic::load(&_tax_rate));
223 Atomic::add(&_budget, tax);
224 }
225
226 intptr_t ShenandoahPacer::epoch() {
227 return Atomic::load(&_epoch);
228 }
229
230 void ShenandoahPacer::pace_for_alloc(size_t words) {
231 assert(ShenandoahPacing, "Only be here when pacing is enabled");
232
233 // Fast path: try to allocate right away
234 if (claim_for_alloc(words, false)) {
235 return;
236 }
237
238 // Threads that are attaching should not block at all: they are not
239 // fully initialized yet. Blocking them would be awkward.
240 // This is probably the path that allocates the thread oop itself.
241 // Forcefully claim without waiting.
242 if (JavaThread::current()->is_attaching_via_jni()) {
243 claim_for_alloc(words, true);
244 return;
245 }
246
247 size_t max = ShenandoahPacingMaxDelay;
248 double start = os::elapsedTime();
249
250 size_t total = 0;
251 size_t cur = 0;
252
253 while (true) {
254 // We could instead assist GC, but this would suffice for now.
255 // This code should also participate in safepointing.
256 // Perform the exponential backoff, limited by max.
257
258 cur = cur * 2;
259 if (total + cur > max) {
260 cur = (max > total) ? (max - total) : 0;
261 }
262 cur = MAX2<size_t>(1, cur);
263
264 wait(cur);
265
266 double end = os::elapsedTime();
267 total = (size_t)((end - start) * 1000);
268
269 if (total > max) {
270 // Spent local time budget to wait for enough GC progress.
271 // Breaking out and allocating anyway, which may mean we outpace GC,
272 // and start Degenerated GC cycle.
273 _delays.add(total);
274
275 // Forcefully claim the budget: it may go negative at this point, and
276 // GC should replenish for this and subsequent allocations
277 claim_for_alloc(words, true);
278 break;
279 }
280
281 if (claim_for_alloc(words, false)) {
282 // Acquired enough permit, nice. Can allocate now.
283 _delays.add(total);
284 break;
285 }
286 }
287 }
288
289 void ShenandoahPacer::wait(long time_ms) {
290 // Perform timed wait. It works like like sleep(), except without modifying
291 // the thread interruptible status. MonitorLocker also checks for safepoints.
292 assert(time_ms > 0, "Should not call this with zero argument, as it would stall until notify");
293 MonitorLocker locker(_wait_monitor);
294 _wait_monitor->wait(time_ms);
295 }
296
297 void ShenandoahPacer::notify_waiters() {
298 MonitorLocker locker(_wait_monitor);
299 _wait_monitor->notify_all();
300 }
301
302 void ShenandoahPacer::print_on(outputStream* out) const {
303 out->print_cr("ALLOCATION PACING:");
|
174
175 size_t ShenandoahPacer::update_and_get_progress_history() {
176 if (_progress == -1) {
177 // First initialization, report some prior
178 Atomic::store(&_progress, (intptr_t)PACING_PROGRESS_ZERO);
179 return (size_t) (_heap->max_capacity() * 0.1);
180 } else {
181 // Record history, and reply historical data
182 _progress_history->add(_progress);
183 Atomic::store(&_progress, (intptr_t)PACING_PROGRESS_ZERO);
184 return (size_t) (_progress_history->avg() * HeapWordSize);
185 }
186 }
187
188 void ShenandoahPacer::restart_with(size_t non_taxable_bytes, double tax_rate) {
189 size_t initial = (size_t)(non_taxable_bytes * tax_rate) >> LogHeapWordSize;
190 STATIC_ASSERT(sizeof(size_t) <= sizeof(intptr_t));
191 Atomic::xchg(&_budget, (intptr_t)initial);
192 Atomic::store(&_tax_rate, tax_rate);
193 Atomic::inc(&_epoch);
194
195 // Shake up stalled waiters after budget update.
196 notify_waiters();
197 }
198
199 bool ShenandoahPacer::claim_for_alloc(size_t words, bool force) {
200 assert(ShenandoahPacing, "Only be here when pacing is enabled");
201
202 intptr_t tax = MAX2<intptr_t>(1, words * Atomic::load(&_tax_rate));
203
204 intptr_t cur = 0;
205 intptr_t new_val = 0;
206 do {
207 cur = Atomic::load(&_budget);
208 if (cur < tax && !force) {
209 // Progress depleted, alas.
210 return false;
211 }
212 new_val = cur - tax;
213 } while (Atomic::cmpxchg(&_budget, cur, new_val) != cur);
214 return true;
215 }
216
217 void ShenandoahPacer::unpace_for_alloc(intptr_t epoch, size_t words) {
218 assert(ShenandoahPacing, "Only be here when pacing is enabled");
219
220 if (_epoch != epoch) {
221 // Stale ticket, no need to unpace.
222 return;
223 }
224
225 intptr_t tax = MAX2<intptr_t>(1, words * Atomic::load(&_tax_rate));
226 Atomic::add(&_budget, tax);
227 }
228
229 intptr_t ShenandoahPacer::epoch() {
230 return Atomic::load(&_epoch);
231 }
232
233 void ShenandoahPacer::pace_for_alloc(size_t words) {
234 assert(ShenandoahPacing, "Only be here when pacing is enabled");
235
236 // Fast path: try to allocate right away
237 bool claimed = claim_for_alloc(words, false);
238 if (claimed) {
239 return;
240 }
241
242 // Forcefully claim the budget: it may go negative at this point, and
243 // GC should replenish for this and subsequent allocations. After this claim,
244 // we would wait a bit until our claim is matched by additional progress,
245 // or the time budget depletes.
246 claimed = claim_for_alloc(words, true);
247 assert(claimed, "Should always succeed");
248
249 // Threads that are attaching should not block at all: they are not
250 // fully initialized yet. Blocking them would be awkward.
251 // This is probably the path that allocates the thread oop itself.
252 if (JavaThread::current()->is_attaching_via_jni()) {
253 return;
254 }
255
256 double start = os::elapsedTime();
257
258 size_t max_ms = ShenandoahPacingMaxDelay;
259 size_t total_ms = 0;
260
261 while (true) {
262 // We could instead assist GC, but this would suffice for now.
263 size_t cur_ms = (max_ms > total_ms) ? (max_ms - total_ms) : 1;
264 wait(cur_ms);
265
266 double end = os::elapsedTime();
267 total_ms = (size_t)((end - start) * 1000);
268
269 if (total_ms > max_ms || Atomic::load(&_budget) >= 0) {
270 // Exiting if either:
271 // a) Spent local time budget to wait for enough GC progress.
272 // Breaking out and allocating anyway, which may mean we outpace GC,
273 // and start Degenerated GC cycle.
274 // b) The budget had been replenished, which means our claim is satisfied.
275 _delays.add(total_ms);
276 break;
277 }
278 }
279 }
280
281 void ShenandoahPacer::wait(long time_ms) {
282 // Perform timed wait. It works like like sleep(), except without modifying
283 // the thread interruptible status. MonitorLocker also checks for safepoints.
284 assert(time_ms > 0, "Should not call this with zero argument, as it would stall until notify");
285 MonitorLocker locker(_wait_monitor);
286 _wait_monitor->wait(time_ms);
287 }
288
289 void ShenandoahPacer::notify_waiters() {
290 MonitorLocker locker(_wait_monitor);
291 _wait_monitor->notify_all();
292 }
293
294 void ShenandoahPacer::print_on(outputStream* out) const {
295 out->print_cr("ALLOCATION PACING:");
|