Skip to content

Commit 21f2e9a

Browse files
author
Kim Barrett
committed
8344332: (bf) Migrate DirectByteBuffer away from jdk.internal.ref.Cleaner
Reviewed-by: rriggs, bchristi
1 parent 854de8c commit 21f2e9a

File tree

9 files changed

+500
-44
lines changed

9 files changed

+500
-44
lines changed

src/java.base/share/classes/java/nio/Bits.java

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,15 @@ static boolean unaligned() {
101101
// increasing delay before throwing OutOfMemoryError:
102102
// 1, 2, 4, 8, 16, 32, 64, 128, 256 (total 511 ms ~ 0.5 s)
103103
// which means that OOME will be thrown after 0.5 s of trying
104+
private static final long INITIAL_SLEEP = 1;
104105
private static final int MAX_SLEEPS = 9;
105106

107+
private static final Object RESERVE_SLOWPATH_LOCK = new Object();
108+
109+
// Token for detecting whether some other thread has done a GC since the
110+
// last time the checking thread went around the retry-with-GC loop.
111+
private static int RESERVE_GC_EPOCH = 0; // Never negative.
112+
106113
// These methods should be called whenever direct memory is allocated or
107114
// freed. They allow the user to control the amount of direct memory
108115
// which a process may access. All sizes are specified in bytes.
@@ -118,29 +125,45 @@ static void reserveMemory(long size, long cap) {
118125
return;
119126
}
120127

121-
final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();
128+
// Don't completely discard interruptions. Instead, record them and
129+
// reapply when we're done here (whether successfully or OOME).
122130
boolean interrupted = false;
123131
try {
124-
125-
// Retry allocation until success or there are no more
126-
// references (including Cleaners that might free direct
127-
// buffer memory) to process and allocation still fails.
128-
boolean refprocActive;
129-
do {
132+
// Keep trying to reserve until either succeed or there is no
133+
// further cleaning available from prior GCs. If the latter then
134+
// GC to hopefully find more cleaning to do. Once a thread GCs it
135+
// drops to the later retry with backoff loop.
136+
for (int cleanedEpoch = -1; true; ) {
137+
synchronized (RESERVE_SLOWPATH_LOCK) {
138+
// Test if cleaning for prior GCs (from here) is complete.
139+
// If so, GC to produce more cleaning work, and change
140+
// the token to inform other threads that there may be
141+
// more cleaning work to do. This is done under the lock
142+
// to close a race. We could have multiple threads pass
143+
// the test "simultaneously", resulting in back-to-back
144+
// GCs. For a STW GC the window is small, but for a
145+
// concurrent GC it's quite large. If a thread were to
146+
// somehow be stuck trying to take the lock while enough
147+
// other threads succeeded for the epoch to wrap, it just
148+
// does an excess GC.
149+
if (RESERVE_GC_EPOCH == cleanedEpoch) {
150+
// Increment with overflow to 0, so the value can
151+
// never equal the initial/reset cleanedEpoch value.
152+
RESERVE_GC_EPOCH = Integer.max(0, RESERVE_GC_EPOCH + 1);
153+
System.gc();
154+
break;
155+
}
156+
cleanedEpoch = RESERVE_GC_EPOCH;
157+
}
130158
try {
131-
refprocActive = jlra.waitForReferenceProcessing();
159+
if (tryReserveOrClean(size, cap)) {
160+
return;
161+
}
132162
} catch (InterruptedException e) {
133-
// Defer interrupts and keep trying.
134163
interrupted = true;
135-
refprocActive = true;
136-
}
137-
if (tryReserveMemory(size, cap)) {
138-
return;
164+
cleanedEpoch = -1; // Reset when incomplete.
139165
}
140-
} while (refprocActive);
141-
142-
// trigger VM's Reference processing
143-
System.gc();
166+
}
144167

145168
// A retry loop with exponential back-off delays.
146169
// Sometimes it would suffice to give up once reference
@@ -151,40 +174,53 @@ static void reserveMemory(long size, long cap) {
151174
// DirectBufferAllocTest to (usually) succeed, while
152175
// without it that test likely fails. Since failure here
153176
// ends in OOME, there's no need to hurry.
154-
long sleepTime = 1;
155-
int sleeps = 0;
156-
while (true) {
157-
if (tryReserveMemory(size, cap)) {
158-
return;
159-
}
160-
if (sleeps >= MAX_SLEEPS) {
161-
break;
162-
}
177+
for (int sleeps = 0; true; ) {
163178
try {
164-
if (!jlra.waitForReferenceProcessing()) {
165-
Thread.sleep(sleepTime);
166-
sleepTime <<= 1;
167-
sleeps++;
179+
if (tryReserveOrClean(size, cap)) {
180+
return;
181+
} else if (sleeps < MAX_SLEEPS) {
182+
Thread.sleep(INITIAL_SLEEP << sleeps);
183+
++sleeps; // Only increment if sleep completed.
184+
} else {
185+
throw new OutOfMemoryError
186+
("Cannot reserve "
187+
+ size + " bytes of direct buffer memory (allocated: "
188+
+ RESERVED_MEMORY.get() + ", limit: " + MAX_MEMORY +")");
168189
}
169190
} catch (InterruptedException e) {
170191
interrupted = true;
171192
}
172193
}
173194

174-
// no luck
175-
throw new OutOfMemoryError
176-
("Cannot reserve "
177-
+ size + " bytes of direct buffer memory (allocated: "
178-
+ RESERVED_MEMORY.get() + ", limit: " + MAX_MEMORY +")");
179-
180195
} finally {
196+
// Reapply any deferred interruption.
181197
if (interrupted) {
182-
// don't swallow interrupts
183198
Thread.currentThread().interrupt();
184199
}
185200
}
186201
}
187202

203+
// Try to reserve memory, or failing that, try to make progress on
204+
// cleaning. Returns true if successfully reserved memory, false if
205+
// failed and ran out of cleaning work.
206+
private static boolean tryReserveOrClean(long size, long cap)
207+
throws InterruptedException
208+
{
209+
JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();
210+
boolean progressing = true;
211+
while (true) {
212+
if (tryReserveMemory(size, cap)) {
213+
return true;
214+
} else if (BufferCleaner.tryCleaning()) {
215+
progressing = true;
216+
} else if (!progressing) {
217+
return false;
218+
} else {
219+
progressing = jlra.waitForReferenceProcessing();
220+
}
221+
}
222+
}
223+
188224
private static boolean tryReserveMemory(long size, long cap) {
189225

190226
// -XX:MaxDirectMemorySize limits the total capacity rather than the

0 commit comments

Comments
 (0)