34
34
35
35
import org .graalvm .nativeimage .CurrentIsolate ;
36
36
import org .graalvm .nativeimage .IsolateThread ;
37
+ import org .graalvm .nativeimage .StackValue ;
37
38
import org .graalvm .nativeimage .c .struct .SizeOf ;
38
39
import org .graalvm .nativeimage .c .type .WordPointer ;
39
40
import org .graalvm .word .Pointer ;
70
71
* and another thread allocates nothing. Between GC 23 and GC 24 the allocation behaviour of these
71
72
* two threads switches. The allocation average and the TLAB size adapt to the new allocation
72
73
* behavior.
73
- *
74
+ *
74
75
* <pre>
75
76
* +-----+---------------------------------------++---------------------------------------+
76
77
* | #GC | Thread 1 || Thread 2 |
87
88
* | 29 | 0B | 270,44kB | 5,41kB || 3,55MB | 3,28MB | 67,14kB |
88
89
* +-----+--------------+------------+-----------++--------------+------------+-----------+
89
90
* </pre>
90
- *
91
+ * <p>
91
92
* A thread allocating a very large amount of memory will also have a high
92
93
* {@link #allocatedBytesAvg}. If such a thread later changes its allocation behaviour and only
93
94
* allocates a small amount of memory the {@link #allocatedBytesAvg} starts decreasing with the next
@@ -109,25 +110,23 @@ public class TlabSupport {
109
110
@ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-23-ga/src/hotspot/share/gc/shared/tlab_globals.hpp#L82-L85" )//
110
111
private static final long TLAB_WASTE_INCREMENT = 4 ;
111
112
112
- // The desired size of the TLAB, including the reserve for filling the unused memory.
113
+ /* The desired size of the TLAB, including the reserve for filling the unused memory. */
113
114
private static final FastThreadLocalWord <UnsignedWord > desiredSize = FastThreadLocalFactory .createWord ("TlabSupport.desiredSize" );
114
-
115
115
private static final FastThreadLocalWord <UnsignedWord > tlabAllocatedAlignedBytesBeforeLastGC = FastThreadLocalFactory .createWord ("TlabSupport.tlabAllocatedAlignedBytesBeforeLastGC" );
116
-
117
116
private static final FastThreadLocalInt numberOfRefills = FastThreadLocalFactory .createInt ("TlabSupport.numberOfRefills" );
118
117
private static final FastThreadLocalInt refillWaste = FastThreadLocalFactory .createInt ("TlabSupport.refillWaste" );
119
118
private static final FastThreadLocalInt gcWaste = FastThreadLocalFactory .createInt ("TlabSupport.gcWaste" );
120
119
121
- // Average of allocated bytes in TLABs of this thread.
120
+ /* Average of allocated bytes in TLABs of this thread. */
122
121
private static final FastThreadLocalBytes <AdaptiveWeightedAverageStruct .Data > allocatedBytesAvg = FastThreadLocalFactory
123
122
.createBytes (() -> SizeOf .get (AdaptiveWeightedAverageStruct .Data .class ), "TlabSupport.allocatedBytesAvg" );
124
123
125
- // Hold onto the TLAB if availableTlabMemory() is larger than this.
124
+ /* Hold onto the TLAB if availableTlabMemory() is larger than this. */
126
125
private static final FastThreadLocalWord <UnsignedWord > refillWasteLimit = FastThreadLocalFactory .createWord ("TlabSupport.refillWasteLimit" );
127
126
128
127
private static final FastThreadLocalInt slowAllocations = FastThreadLocalFactory .createInt ("TlabSupport.slowAllocations" );
129
128
130
- // Expected number of refills between GCs.
129
+ /* Expected number of refills between GCs. */
131
130
private static UnsignedWord targetRefills = Word .unsigned (1 );
132
131
133
132
private static boolean initialized ;
@@ -160,9 +159,53 @@ public static void initialize(IsolateThread thread) {
160
159
resetStatistics (thread );
161
160
}
162
161
162
+ @ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-25+25/src/hotspot/share/gc/shared/memAllocator.cpp#L257-L329" )
163
+ @ Uninterruptible (reason = "Holds uninitialized memory." )
164
+ static Pointer allocateRawMemoryInTlabSlow (UnsignedWord size ) {
165
+ ThreadLocalAllocation .Descriptor tlab = getTlab ();
166
+
167
+ /*
168
+ * Retain tlab and allocate object as an heap allocation if the amount free in the tlab is
169
+ * too large to discard.
170
+ */
171
+ if (shouldRetainTlab (tlab )) {
172
+ recordSlowAllocation ();
173
+ return Word .nullPointer ();
174
+ }
175
+
176
+ /* Discard tlab and allocate a new one. */
177
+ recordRefillWaste ();
178
+ retireTlab (CurrentIsolate .getCurrentThread (), false );
179
+
180
+ /* To minimize fragmentation, the last tlab may be smaller than the rest. */
181
+ UnsignedWord newTlabSize = computeSizeOfNewTlab (size );
182
+ if (newTlabSize .equal (0 )) {
183
+ return Word .nullPointer ();
184
+ }
185
+
186
+ /*
187
+ * Allocate a new TLAB requesting newTlabSize. Any size between minimal and newTlabSize is
188
+ * accepted.
189
+ */
190
+ UnsignedWord computedMinSize = computeMinSizeOfNewTlab (size );
191
+
192
+ WordPointer allocatedTlabSize = StackValue .get (WordPointer .class );
193
+ Pointer memory = YoungGeneration .getHeapAllocation ().allocateNewTlab (computedMinSize , newTlabSize , allocatedTlabSize );
194
+ if (memory .isNull ()) {
195
+ assert Word .unsigned (0 ).equal (allocatedTlabSize .read ()) : "Allocation failed, but actual size was updated." ;
196
+ return Word .nullPointer ();
197
+ }
198
+ assert Word .unsigned (0 ).notEqual (allocatedTlabSize .read ()) : "Allocation succeeded but actual size not updated." ;
199
+
200
+ fillTlab (memory , memory .add (size ), allocatedTlabSize );
201
+ return memory ;
202
+ }
203
+
204
+ @ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-25+25/src/hotspot/share/runtime/thread.cpp#L168-L174" )
163
205
@ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-23-ga/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp#L183-L195" )
164
206
@ Uninterruptible (reason = "Accesses TLAB" )
165
- static void fillTlab (Pointer start , Pointer top , WordPointer newSize ) {
207
+ private static void fillTlab (Pointer start , Pointer top , WordPointer newSize ) {
208
+ /* Fill the TLAB. */
166
209
numberOfRefills .set (numberOfRefills .get () + 1 );
167
210
168
211
Pointer hardEnd = start .add (newSize .read ());
@@ -172,39 +215,38 @@ static void fillTlab(Pointer start, Pointer top, WordPointer newSize) {
172
215
173
216
initialize (getTlab (), start , top , end );
174
217
175
- // Reset amount of internal fragmentation
218
+ /* Reset amount of internal fragmentation. */
176
219
refillWasteLimit .set (initialRefillWasteLimit ());
177
220
}
178
221
179
222
@ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-25+25/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp#L143-L145" )
180
223
@ Uninterruptible (reason = "Accesses TLAB" )
181
- static void retireTlabBeforeAllocation () {
224
+ private static void recordRefillWaste () {
182
225
long availableTlabMemory = availableTlabMemory (getTlab ()).rawValue ();
183
226
refillWaste .set (refillWaste .get () + UninterruptibleUtils .NumUtil .safeToInt (availableTlabMemory ));
184
- retireCurrentTlab (CurrentIsolate .getCurrentThread (), false );
185
227
}
186
228
229
+ @ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-25+25/src/hotspot/share/runtime/thread.cpp#L157-L166" )
187
230
@ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-25+25/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp#L131-L141" )
188
231
@ Uninterruptible (reason = "Accesses TLAB" )
189
- private static void retireCurrentTlab (IsolateThread thread , boolean calculateStats ) {
232
+ private static void retireTlab (IsolateThread thread , boolean calculateStats ) {
233
+ /* Sampling and serviceability support. */
190
234
ThreadLocalAllocation .Descriptor tlab = getTlab (thread );
191
-
192
235
if (tlab .getAllocationEnd (TLAB_END_IDENTITY ).isNonNull ()) {
193
- assert checkInvariants (tlab );
194
-
195
- UnsignedWord usedTlabSize = getUsedTlabSize (tlab );
196
- allocatedAlignedBytes .set (thread , allocatedAlignedBytes .get (thread ).add (usedTlabSize ));
197
- insertFiller (tlab );
198
- initialize (tlab , Word .nullPointer (), Word .nullPointer (), Word .nullPointer ());
236
+ UnsignedWord usedBytes = getUsedTlabSize (tlab );
237
+ allocatedAlignedBytes .set (thread , allocatedAlignedBytes .get (thread ).add (usedBytes ));
199
238
}
200
239
201
- /*
202
- * Collect statistics after the TLAB has been retired. Otherwise, the current TLAB is
203
- * excluded from the statistics.
204
- */
240
+ /* Retire the TLAB. */
205
241
if (calculateStats ) {
206
242
accumulateAndResetStatistics (thread );
207
243
}
244
+
245
+ if (tlab .getAllocationEnd (TLAB_END_IDENTITY ).isNonNull ()) {
246
+ assert checkInvariants (tlab );
247
+ insertFiller (tlab );
248
+ initialize (tlab , Word .nullPointer (), Word .nullPointer (), Word .nullPointer ());
249
+ }
208
250
}
209
251
210
252
@ Uninterruptible (reason = "Accesses TLAB" )
@@ -238,7 +280,7 @@ private static boolean checkInvariants(Descriptor tlab) {
238
280
@ Uninterruptible (reason = "Accesses TLAB" )
239
281
static void suspendAllocationInCurrentThread () {
240
282
/* The statistics for this thread will be updated later. */
241
- retireCurrentTlab (CurrentIsolate .getCurrentThread (), false );
283
+ retireTlab (CurrentIsolate .getCurrentThread (), false );
242
284
}
243
285
244
286
@ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
@@ -268,7 +310,7 @@ static void disableAndFlushForThread(IsolateThread vmThread) {
268
310
private static void retireTlabToEden (IsolateThread thread ) {
269
311
VMThreads .guaranteeOwnsThreadMutex ("Otherwise, we wouldn't be allowed to access the space." , true );
270
312
271
- retireCurrentTlab (thread , true );
313
+ retireTlab (thread , true );
272
314
273
315
Descriptor tlab = getTlab (thread );
274
316
UnalignedHeapChunk .UnalignedHeader unalignedChunk = tlab .getUnalignedChunk ();
@@ -285,7 +327,7 @@ private static void retireTlabToEden(IsolateThread thread) {
285
327
}
286
328
287
329
@ Uninterruptible (reason = "Accesses TLAB" )
288
- static UnsignedWord availableTlabMemory (Descriptor tlab ) {
330
+ private static UnsignedWord availableTlabMemory (Descriptor tlab ) {
289
331
Pointer top = tlab .getAllocationTop (TLAB_TOP_IDENTITY );
290
332
Pointer end = tlab .getAllocationEnd (TLAB_END_IDENTITY );
291
333
assert top .belowOrEqual (end );
@@ -323,7 +365,6 @@ private static void insertFiller(ThreadLocalAllocation.Descriptor tlab) {
323
365
if (top .belowThan (hardEnd )) {
324
366
FillerObjectUtil .writeFillerObjectAt (top , size );
325
367
}
326
-
327
368
}
328
369
329
370
@ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-23-ga/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp#L175-L181" )
@@ -385,7 +426,7 @@ private static UnsignedWord initialRefillWasteLimit() {
385
426
386
427
@ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-25+8/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp#L54-L71" )
387
428
@ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
388
- static UnsignedWord computeSizeOfNewTlab (UnsignedWord allocationSize ) {
429
+ private static UnsignedWord computeSizeOfNewTlab (UnsignedWord allocationSize ) {
389
430
assert UnsignedUtils .isAMultiple (allocationSize , Word .unsigned (ConfigurationValues .getObjectLayout ().getAlignment ()));
390
431
391
432
/*
@@ -404,7 +445,7 @@ static UnsignedWord computeSizeOfNewTlab(UnsignedWord allocationSize) {
404
445
405
446
@ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-23-ga/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp#L73-L77" )
406
447
@ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
407
- static UnsignedWord computeMinSizeOfNewTlab (UnsignedWord allocationSize ) {
448
+ private static UnsignedWord computeMinSizeOfNewTlab (UnsignedWord allocationSize ) {
408
449
UnsignedWord alignedSize = Word .unsigned (ConfigurationValues .getObjectLayout ().alignUp (allocationSize .rawValue ()));
409
450
UnsignedWord sizeWithReserve = alignedSize .add (getFillerObjectSize ());
410
451
long minTlabSize = TlabOptionCache .singleton ().getMinTlabSize ();
@@ -413,13 +454,13 @@ static UnsignedWord computeMinSizeOfNewTlab(UnsignedWord allocationSize) {
413
454
}
414
455
415
456
@ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
416
- static boolean shouldRetainTlab (Descriptor tlab ) {
457
+ private static boolean shouldRetainTlab (Descriptor tlab ) {
417
458
return availableTlabMemory (tlab ).aboveThan (refillWasteLimit .get ());
418
459
}
419
460
420
461
@ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-25+11/src/hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp#L79-L94" )
421
462
@ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
422
- static void recordSlowAllocation () {
463
+ private static void recordSlowAllocation () {
423
464
/*
424
465
* Raise size required to bypass TLAB next time. Else there's a risk that a thread that
425
466
* repeatedly allocates objects of one size will get stuck on this slow path.
@@ -436,15 +477,16 @@ static UnsignedWord maxSize() {
436
477
@ BasedOnJDKFile ("https://github.com/openjdk/jdk/blob/jdk-23-ga/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp#L76-L117" )
437
478
@ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
438
479
private static void accumulateAndResetStatistics (IsolateThread thread ) {
439
- gcWaste .set (thread , gcWaste .get () + UninterruptibleUtils .NumUtil .safeToInt (availableTlabMemory (getTlab (thread )).rawValue ()));
480
+ UnsignedWord remaining = availableTlabMemory (getTlab ());
481
+ gcWaste .set (thread , gcWaste .get () + UnsignedUtils .safeToInt (remaining ));
482
+
440
483
UnsignedWord totalAlignedAllocated = ThreadLocalAllocation .getAlignedAllocatedBytes (thread );
441
484
UnsignedWord allocatedAlignedSinceLastGC = totalAlignedAllocated .subtract (tlabAllocatedAlignedBytesBeforeLastGC .get (thread ));
442
485
tlabAllocatedAlignedBytesBeforeLastGC .set (thread , totalAlignedAllocated );
443
486
444
487
AdaptiveWeightedAverageStruct .sample (allocatedBytesAvg .getAddress (thread ), allocatedAlignedSinceLastGC .rawValue ());
445
488
446
489
printStats (thread , allocatedAlignedSinceLastGC );
447
-
448
490
resetStatistics (thread );
449
491
}
450
492
@@ -469,7 +511,6 @@ static void logTlabChunks(Log log, IsolateThread thread, String shortSpaceName)
469
511
ThreadLocalAllocation .Descriptor tlab = getTlabUnsafe (thread );
470
512
471
513
// Aligned chunks are handled in HeapAllocation.
472
-
473
514
UnalignedHeapChunk .UnalignedHeader uChunk = tlab .getUnalignedChunk ();
474
515
HeapChunkLogging .logChunks (log , uChunk , shortSpaceName , false );
475
516
}
0 commit comments