2424 */
2525package com .oracle .svm .core .genscavenge ;
2626
27- import java .util .concurrent .atomic .AtomicBoolean ;
28-
29- import jdk .graal .compiler .word .Word ;
3027import org .graalvm .nativeimage .Platform ;
3128import org .graalvm .nativeimage .Platforms ;
3229import org .graalvm .word .UnsignedWord ;
3633import com .oracle .svm .core .heap .PhysicalMemory ;
3734import com .oracle .svm .core .heap .ReferenceAccess ;
3835import com .oracle .svm .core .jdk .UninterruptibleUtils ;
36+ import com .oracle .svm .core .os .CommittedMemoryProvider ;
37+ import com .oracle .svm .core .thread .JavaSpinLockUtils ;
3938import com .oracle .svm .core .thread .VMOperation ;
4039import com .oracle .svm .core .util .UnsignedUtils ;
4140import com .oracle .svm .core .util .VMError ;
4241
4342import jdk .graal .compiler .api .replacements .Fold ;
44- import jdk .graal .compiler .nodes .PauseNode ;
43+ import jdk .graal .compiler .word .Word ;
44+ import jdk .internal .misc .Unsafe ;
4545
46+ /**
47+ * Note that a lot of methods in this class are final. Subclasses may only override certain methods
48+ * to avoid inconsistencies between the different heap size values.
49+ */
4650abstract class AbstractCollectionPolicy implements CollectionPolicy {
4751
4852 protected static final int MIN_SPACE_SIZE_IN_ALIGNED_CHUNKS = 8 ;
@@ -54,6 +58,9 @@ static int getMaxSurvivorSpaces(Integer userValue) {
5458 return (userValue != null ) ? userValue : AbstractCollectionPolicy .MAX_TENURING_THRESHOLD ;
5559 }
5660
61+ private static final Unsafe U = Unsafe .getUnsafe ();
62+ private static final long SIZES_UPDATE_LOCK_OFFSET = U .objectFieldOffset (AbstractCollectionPolicy .class , "sizesUpdateLock" );
63+
5764 /*
5865 * Constants that can be made options if desirable. These are -XX options in HotSpot, refer to
5966 * their descriptions for details. The values are HotSpot defaults unless labeled otherwise.
@@ -78,8 +85,8 @@ static int getMaxSurvivorSpaces(Integer userValue) {
7885 protected UnsignedWord oldSize ;
7986 protected int tenuringThreshold ;
8087
81- protected volatile SizeParameters sizes ;
82- private final AtomicBoolean sizesUpdateSpinLock = new AtomicBoolean () ;
88+ protected volatile SizeParameters sizes = null ;
89+ @ SuppressWarnings ( "unused" ) private volatile int sizesUpdateLock ;
8390
8491 protected AbstractCollectionPolicy (int initialNewRatio , int initialTenuringThreshold ) {
8592 this .initialNewRatio = initialNewRatio ;
@@ -145,40 +152,49 @@ protected void guaranteeSizeParametersInitialized() {
145152
146153 @ Override
147154 public void updateSizeParameters () {
148- SizeParameters params = computeSizeParameters (sizes );
149- SizeParameters previous = sizes ;
150- if (previous != null && params .equal (previous )) {
155+ /*
156+ * Read the old object before computing the new values. Otherwise, we risk reusing an
157+ * outdated SizeParameters object.
158+ */
159+ SizeParameters prevParams = sizes ;
160+ SizeParameters newParams = computeSizeParameters (prevParams );
161+ if (prevParams != null && newParams .equal (prevParams )) {
151162 return ; // nothing to do
152163 }
153- while (!sizesUpdateSpinLock .compareAndSet (false , true )) {
154- /*
155- * We use a primitive spin lock because at this point, the current thread might be
156- * unable to use a Java lock (e.g. no Thread object yet), and the critical section is
157- * short, so we do not want to suspend and wake up threads for it.
158- */
159- PauseNode .pause ();
160- }
164+ updateSizeParameters0 (newParams , prevParams );
165+ guaranteeSizeParametersInitialized (); // sanity
166+ }
167+
168+ @ Uninterruptible (reason = "Holding the spin lock at a safepoint can result in deadlocks." )
169+ private void updateSizeParameters0 (SizeParameters newParams , SizeParameters prevParams ) {
170+ /*
171+ * We use a primitive spin lock because at this point, the current thread might be unable to
172+ * use a Java lock (e.g. no Thread object yet), and the critical section is short, so we do
173+ * not want to suspend and wake up threads for it.
174+ */
175+ JavaSpinLockUtils .lockNoTransition (this , SIZES_UPDATE_LOCK_OFFSET );
161176 try {
162- updateSizeParametersLocked (params , previous );
177+ if (sizes != prevParams ) {
178+ /*
179+ * Some other thread beat us and we cannot tell if our values or their values are
180+ * newer, so back off - any newer values will be applied eventually.
181+ */
182+ return ;
183+ }
184+ updateSizeParametersLocked (newParams , prevParams );
163185 } finally {
164- sizesUpdateSpinLock . set ( false );
186+ JavaSpinLockUtils . unlock ( this , SIZES_UPDATE_LOCK_OFFSET );
165187 }
166- guaranteeSizeParametersInitialized (); // sanity
167188 }
168189
169- @ Uninterruptible (reason = "Must be atomic with regard to garbage collection." )
170- private void updateSizeParametersLocked (SizeParameters params , SizeParameters previous ) {
171- if (sizes != previous ) {
172- // Some other thread beat us and we cannot tell if our values or their values are newer,
173- // so back off -- any newer values will be applied eventually.
174- return ;
175- }
176- sizes = params ;
190+ @ Uninterruptible (reason = "Holding the spin lock at a safepoint can result in deadlocks. Updating the size parameters must be atomic with regard to garbage collection." )
191+ private void updateSizeParametersLocked (SizeParameters newParams , SizeParameters prevParams ) {
192+ sizes = newParams ;
177193
178- if (previous == null || gcCount () == 0 ) {
179- survivorSize = params .initialSurvivorSize ;
180- edenSize = params .initialEdenSize ;
181- oldSize = params .initialOldSize ();
194+ if (prevParams == null || gcCount () == 0 ) {
195+ survivorSize = newParams .initialSurvivorSize ;
196+ edenSize = newParams .initialEdenSize ;
197+ oldSize = newParams .initialOldSize ();
182198 promoSize = UnsignedUtils .min (edenSize , oldSize );
183199 }
184200
@@ -191,85 +207,85 @@ private void updateSizeParametersLocked(SizeParameters params, SizeParameters pr
191207 * We assume that such changes happen very early on and values then adapt reasonably quick,
192208 * but we must still ensure that computations can handle it (for example, no overflows).
193209 */
194- survivorSize = UnsignedUtils .min (survivorSize , params .maxSurvivorSize ());
210+ survivorSize = UnsignedUtils .min (survivorSize , newParams .maxSurvivorSize ());
195211 edenSize = UnsignedUtils .min (edenSize , getMaximumEdenSize ());
196- oldSize = UnsignedUtils .min (oldSize , params .maxOldSize ());
197- promoSize = UnsignedUtils .min (promoSize , params .maxOldSize ());
212+ oldSize = UnsignedUtils .min (oldSize , newParams .maxOldSize ());
213+ promoSize = UnsignedUtils .min (promoSize , newParams .maxOldSize ());
198214 }
199215
200216 @ Override
201- public UnsignedWord getInitialEdenSize () {
217+ public final UnsignedWord getInitialEdenSize () {
202218 guaranteeSizeParametersInitialized ();
203219 return sizes .initialEdenSize ;
204220 }
205221
206222 @ Override
207223 @ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
208- public UnsignedWord getMaximumEdenSize () {
224+ public final UnsignedWord getMaximumEdenSize () {
209225 guaranteeSizeParametersInitialized ();
210226 return alignDown (sizes .maxYoungSize .subtract (survivorSize .multiply (2 )));
211227 }
212228
213229 @ Override
214- public UnsignedWord getMaximumHeapSize () {
230+ public final UnsignedWord getMaximumHeapSize () {
215231 guaranteeSizeParametersInitialized ();
216232 return sizes .maxHeapSize ;
217233 }
218234
219235 @ Override
220- public UnsignedWord getMaximumYoungGenerationSize () {
236+ public final UnsignedWord getMaximumYoungGenerationSize () {
221237 guaranteeSizeParametersInitialized ();
222238 return sizes .maxYoungSize ;
223239 }
224240
225241 @ Override
226- public UnsignedWord getInitialSurvivorSize () {
242+ public final UnsignedWord getInitialSurvivorSize () {
227243 guaranteeSizeParametersInitialized ();
228244 return sizes .initialSurvivorSize ;
229245 }
230246
231247 @ Override
232- public UnsignedWord getMaximumSurvivorSize () {
248+ public final UnsignedWord getMaximumSurvivorSize () {
233249 guaranteeSizeParametersInitialized ();
234250 return sizes .maxSurvivorSize ();
235251 }
236252
237253 @ Override
238- public UnsignedWord getCurrentHeapCapacity () {
254+ public final UnsignedWord getCurrentHeapCapacity () {
239255 assert VMOperation .isGCInProgress () : "use only during GC" ;
240256 guaranteeSizeParametersInitialized ();
241257 return edenSize .add (survivorSize ).add (oldSize );
242258 }
243259
244260 @ Override
245261 @ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
246- public UnsignedWord getSurvivorSpacesCapacity () {
262+ public final UnsignedWord getSurvivorSpacesCapacity () {
247263 assert VMOperation .isGCInProgress () : "use only during GC" ;
248264 guaranteeSizeParametersInitialized ();
249265 return survivorSize ;
250266 }
251267
252268 @ Override
253269 @ Uninterruptible (reason = "Ensure reading a consistent value." )
254- public UnsignedWord getYoungGenerationCapacity () {
270+ public final UnsignedWord getYoungGenerationCapacity () {
255271 guaranteeSizeParametersInitialized ();
256272 return edenSize .add (survivorSize );
257273 }
258274
259275 @ Override
260- public UnsignedWord getInitialOldSize () {
276+ public final UnsignedWord getInitialOldSize () {
261277 guaranteeSizeParametersInitialized ();
262278 return sizes .initialOldSize ();
263279 }
264280
265281 @ Override
266- public UnsignedWord getMaximumOldSize () {
282+ public final UnsignedWord getMaximumOldSize () {
267283 guaranteeSizeParametersInitialized ();
268284 return sizes .maxOldSize ();
269285 }
270286
271287 @ Override
272- public UnsignedWord getOldGenerationCapacity () {
288+ public final UnsignedWord getOldGenerationCapacity () {
273289 guaranteeSizeParametersInitialized ();
274290 return oldSize ;
275291 }
@@ -308,15 +324,14 @@ public void onCollectionBegin(boolean completeCollection, long requestingNanoTim
308324 }
309325
310326 @ Override
311- public UnsignedWord getMinimumHeapSize () {
327+ public final UnsignedWord getMinimumHeapSize () {
312328 return sizes .minHeapSize ;
313329 }
314330
315331 @ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
316332 protected abstract long gcCount ();
317333
318334 protected SizeParameters computeSizeParameters (SizeParameters existing ) {
319- UnsignedWord addressSpaceSize = ReferenceAccess .singleton ().getAddressSpaceSize ();
320335 UnsignedWord minYoungSpaces = minSpaceSize (); // eden
321336 if (HeapParameters .getMaxSurvivorSpaces () > 0 ) {
322337 minYoungSpaces = minYoungSpaces .add (minSpaceSize ().multiply (2 )); // survivor from and to
@@ -331,7 +346,7 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) {
331346 maxHeap = PhysicalMemory .size ().unsignedDivide (100 ).multiply (HeapParameters .getMaximumHeapSizePercent ());
332347 }
333348 UnsignedWord unadjustedMaxHeap = maxHeap ;
334- maxHeap = UnsignedUtils .clamp (alignDown (maxHeap ), minAllSpaces , alignDown (addressSpaceSize ));
349+ maxHeap = UnsignedUtils .clamp (alignDown (maxHeap ), minAllSpaces , alignDown (getHeapSizeLimit () ));
335350
336351 UnsignedWord maxYoung ;
337352 long optionMaxYoung = SubstrateGCOptions .MaxNewSize .getValue ();
@@ -342,11 +357,11 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) {
342357 } else {
343358 maxYoung = maxHeap .unsignedDivide (AbstractCollectionPolicy .NEW_RATIO + 1 );
344359 }
345- maxYoung = UnsignedUtils .clamp (alignDown (maxYoung ), minYoungSpaces , maxHeap . subtract ( minSpaceSize () ));
360+ maxYoung = UnsignedUtils .clamp (alignDown (maxYoung ), minYoungSpaces , getYoungSizeLimit ( maxHeap ));
346361
347362 UnsignedWord maxOld = alignDown (maxHeap .subtract (maxYoung ));
348363 maxHeap = maxYoung .add (maxOld );
349- VMError .guarantee (maxOld .aboveOrEqual (minSpaceSize ()) && maxHeap .belowOrEqual (addressSpaceSize ) &&
364+ VMError .guarantee (maxOld .aboveOrEqual (minSpaceSize ()) && maxHeap .belowOrEqual (getHeapSizeLimit () ) &&
350365 (maxHeap .belowOrEqual (unadjustedMaxHeap ) || unadjustedMaxHeap .belowThan (minAllSpaces )));
351366
352367 UnsignedWord minHeap = Word .zero ();
@@ -384,6 +399,14 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) {
384399 return SizeParameters .get (existing , maxHeap , maxYoung , initialHeap , initialEden , initialSurvivor , minHeap );
385400 }
386401
402+ protected UnsignedWord getHeapSizeLimit () {
403+ return CommittedMemoryProvider .get ().getCollectedHeapAddressSpaceSize ();
404+ }
405+
406+ protected UnsignedWord getYoungSizeLimit (UnsignedWord maxHeap ) {
407+ return maxHeap .subtract (minSpaceSize ());
408+ }
409+
387410 protected UnsignedWord getInitialHeapSize () {
388411 return AbstractCollectionPolicy .INITIAL_HEAP_SIZE ;
389412 }
@@ -419,7 +442,7 @@ private SizeParameters(UnsignedWord maxHeapSize, UnsignedWord maxYoungSize, Unsi
419442 assert initialHeapSize .belowOrEqual (maxHeapSize );
420443 assert maxSurvivorSize ().belowThan (maxYoungSize );
421444 assert maxYoungSize .add (maxOldSize ()).equal (maxHeapSize );
422- assert maxHeapSize .belowOrEqual (ReferenceAccess .singleton ().getAddressSpaceSize ());
445+ assert maxHeapSize .belowOrEqual (ReferenceAccess .singleton ().getMaxAddressSpaceSize ());
423446 assert initialEdenSize .add (initialSurvivorSize .multiply (2 )).equal (initialYoungSize ());
424447 assert initialYoungSize ().add (initialOldSize ()).equal (initialHeapSize );
425448 }
0 commit comments