Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/rmi/transport/DGCClient.java
38831 views
1
/*
2
* Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
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. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package sun.rmi.transport;
26
27
import java.io.InvalidClassException;
28
import java.lang.ref.PhantomReference;
29
import java.lang.ref.ReferenceQueue;
30
import java.net.SocketPermission;
31
import java.rmi.UnmarshalException;
32
import java.security.AccessController;
33
import java.security.PrivilegedAction;
34
import java.util.HashMap;
35
import java.util.HashSet;
36
import java.util.Iterator;
37
import java.util.List;
38
import java.util.Map;
39
import java.util.Set;
40
import java.rmi.ConnectException;
41
import java.rmi.RemoteException;
42
import java.rmi.dgc.DGC;
43
import java.rmi.dgc.Lease;
44
import java.rmi.dgc.VMID;
45
import java.rmi.server.ObjID;
46
47
import sun.misc.GC;
48
import sun.rmi.runtime.Log;
49
import sun.rmi.runtime.NewThreadAction;
50
import sun.rmi.server.UnicastRef;
51
import sun.rmi.server.Util;
52
import sun.security.action.GetLongAction;
53
54
import java.security.AccessControlContext;
55
import java.security.Permissions;
56
import java.security.ProtectionDomain;
57
58
/**
59
* DGCClient implements the client-side of the RMI distributed garbage
60
* collection system.
61
*
62
* The external interface to DGCClient is the "registerRefs" method.
63
* When a LiveRef to a remote object enters the VM, it needs to be
64
* registered with the DGCClient to participate in distributed garbage
65
* collection.
66
*
67
* When the first LiveRef to a particular remote object is registered,
68
* a "dirty" call is made to the server-side distributed garbage
69
* collector for the remote object, which returns a lease guaranteeing
70
* that the server-side DGC will not collect the remote object for a
71
* certain period of time. While LiveRef instances to remote objects
72
* on a particular server exist, the DGCClient periodically sends more
73
* "dirty" calls to renew its lease.
74
*
75
* The DGCClient tracks the local reachability of registered LiveRef
76
* instances (using phantom references). When the LiveRef instance
77
* for a particular remote object becomes garbage collected locally,
78
* a "clean" call is made to the server-side distributed garbage
79
* collector, indicating that the server no longer needs to keep the
80
* remote object alive for this client.
81
*
82
* @see java.rmi.dgc.DGC, sun.rmi.transport.DGCImpl
83
*
84
* @author Ann Wollrath
85
* @author Peter Jones
86
*/
87
final class DGCClient {
88
89
/** next sequence number for DGC calls (access synchronized on class) */
90
private static long nextSequenceNum = Long.MIN_VALUE;
91
92
/** unique identifier for this VM as a client of DGC */
93
private static VMID vmid = new VMID();
94
95
/** lease duration to request (usually ignored by server) */
96
private static final long leaseValue = // default 10 minutes
97
AccessController.doPrivileged(
98
new GetLongAction("java.rmi.dgc.leaseValue",
99
600000)).longValue();
100
101
/** maximum interval between retries of failed clean calls */
102
private static final long cleanInterval = // default 3 minutes
103
AccessController.doPrivileged(
104
new GetLongAction("sun.rmi.dgc.cleanInterval",
105
180000)).longValue();
106
107
/** maximum interval between complete garbage collections of local heap */
108
private static final long gcInterval = // default 1 hour
109
AccessController.doPrivileged(
110
new GetLongAction("sun.rmi.dgc.client.gcInterval",
111
3600000)).longValue();
112
113
/** minimum retry count for dirty calls that fail */
114
private static final int dirtyFailureRetries = 5;
115
116
/** retry count for clean calls that fail with ConnectException */
117
private static final int cleanFailureRetries = 5;
118
119
/** constant empty ObjID array for lease renewal optimization */
120
private static final ObjID[] emptyObjIDArray = new ObjID[0];
121
122
/** ObjID for server-side DGC object */
123
private static final ObjID dgcID = new ObjID(ObjID.DGC_ID);
124
125
/**
126
* An AccessControlContext with only socket permissions,
127
* suitable for an RMIClientSocketFactory.
128
*/
129
private static final AccessControlContext SOCKET_ACC;
130
static {
131
Permissions perms = new Permissions();
132
perms.add(new SocketPermission("*", "connect,resolve"));
133
ProtectionDomain[] pd = { new ProtectionDomain(null, perms) };
134
SOCKET_ACC = new AccessControlContext(pd);
135
}
136
137
/*
138
* Disallow anyone from creating one of these.
139
*/
140
private DGCClient() {}
141
142
/**
143
* Register the LiveRef instances in the supplied list to participate
144
* in distributed garbage collection.
145
*
146
* All of the LiveRefs in the list must be for remote objects at the
147
* given endpoint.
148
*/
149
static void registerRefs(Endpoint ep, List<LiveRef> refs) {
150
/*
151
* Look up the given endpoint and register the refs with it.
152
* The retrieved entry may get removed from the global endpoint
153
* table before EndpointEntry.registerRefs() is able to acquire
154
* its lock; in this event, it returns false, and we loop and
155
* try again.
156
*/
157
EndpointEntry epEntry;
158
do {
159
epEntry = EndpointEntry.lookup(ep);
160
} while (!epEntry.registerRefs(refs));
161
}
162
163
/**
164
* Get the next sequence number to be used for a dirty or clean
165
* operation from this VM. This method should only be called while
166
* synchronized on the EndpointEntry whose data structures the
167
* operation affects.
168
*/
169
private static synchronized long getNextSequenceNum() {
170
return nextSequenceNum++;
171
}
172
173
/**
174
* Given the length of a lease and the time that it was granted,
175
* compute the absolute time at which it should be renewed, giving
176
* room for reasonable computational and communication delays.
177
*/
178
private static long computeRenewTime(long grantTime, long duration) {
179
/*
180
* REMIND: This algorithm should be more sophisticated, waiting
181
* a longer fraction of the lease duration for longer leases.
182
*/
183
return grantTime + (duration / 2);
184
}
185
186
/**
187
* EndpointEntry encapsulates the client-side DGC information specific
188
* to a particular Endpoint. Of most significance is the table that
189
* maps LiveRef value to RefEntry objects and the renew/clean thread
190
* that handles asynchronous client-side DGC operations.
191
*/
192
private static class EndpointEntry {
193
194
/** the endpoint that this entry is for */
195
private Endpoint endpoint;
196
/** synthesized reference to the remote server-side DGC */
197
private DGC dgc;
198
199
/** table of refs held for endpoint: maps LiveRef to RefEntry */
200
private Map<LiveRef, RefEntry> refTable = new HashMap<>(5);
201
/** set of RefEntry instances from last (failed) dirty call */
202
private Set<RefEntry> invalidRefs = new HashSet<>(5);
203
204
/** true if this entry has been removed from the global table */
205
private boolean removed = false;
206
207
/** absolute time to renew current lease to this endpoint */
208
private long renewTime = Long.MAX_VALUE;
209
/** absolute time current lease to this endpoint will expire */
210
private long expirationTime = Long.MIN_VALUE;
211
/** count of recent dirty calls that have failed */
212
private int dirtyFailures = 0;
213
/** absolute time of first recent failed dirty call */
214
private long dirtyFailureStartTime;
215
/** (average) elapsed time for recent failed dirty calls */
216
private long dirtyFailureDuration;
217
218
/** renew/clean thread for handling lease renewals and clean calls */
219
private Thread renewCleanThread;
220
/** true if renew/clean thread may be interrupted */
221
private boolean interruptible = false;
222
223
/** reference queue for phantom references */
224
private ReferenceQueue<LiveRef> refQueue = new ReferenceQueue<>();
225
/** set of clean calls that need to be made */
226
private Set<CleanRequest> pendingCleans = new HashSet<>(5);
227
228
/** global endpoint table: maps Endpoint to EndpointEntry */
229
private static Map<Endpoint,EndpointEntry> endpointTable = new HashMap<>(5);
230
/** handle for GC latency request (for future cancellation) */
231
private static GC.LatencyRequest gcLatencyRequest = null;
232
233
/**
234
* Look up the EndpointEntry for the given Endpoint. An entry is
235
* created if one does not already exist.
236
*/
237
public static EndpointEntry lookup(Endpoint ep) {
238
synchronized (endpointTable) {
239
EndpointEntry entry = endpointTable.get(ep);
240
if (entry == null) {
241
entry = new EndpointEntry(ep);
242
endpointTable.put(ep, entry);
243
/*
244
* While we are tracking live remote references registered
245
* in this VM, request a maximum latency for inspecting the
246
* entire heap from the local garbage collector, to place
247
* an upper bound on the time to discover remote references
248
* that have become unreachable (see bugid 4171278).
249
*/
250
if (gcLatencyRequest == null) {
251
gcLatencyRequest = GC.requestLatency(gcInterval);
252
}
253
}
254
return entry;
255
}
256
}
257
258
private EndpointEntry(final Endpoint endpoint) {
259
this.endpoint = endpoint;
260
try {
261
LiveRef dgcRef = new LiveRef(dgcID, endpoint, false);
262
dgc = (DGC) Util.createProxy(DGCImpl.class,
263
new UnicastRef(dgcRef), true);
264
} catch (RemoteException e) {
265
throw new Error("internal error creating DGC stub");
266
}
267
renewCleanThread = AccessController.doPrivileged(
268
new NewThreadAction(new RenewCleanThread(),
269
"RenewClean-" + endpoint, true));
270
renewCleanThread.start();
271
}
272
273
/**
274
* Register the LiveRef instances in the supplied list to participate
275
* in distributed garbage collection.
276
*
277
* This method returns false if this entry was removed from the
278
* global endpoint table (because it was empty) before these refs
279
* could be registered. In that case, a new EndpointEntry needs
280
* to be looked up.
281
*
282
* This method must NOT be called while synchronized on this entry.
283
*/
284
public boolean registerRefs(List<LiveRef> refs) {
285
assert !Thread.holdsLock(this);
286
287
Set<RefEntry> refsToDirty = null; // entries for refs needing dirty
288
long sequenceNum; // sequence number for dirty call
289
290
synchronized (this) {
291
if (removed) {
292
return false;
293
}
294
295
Iterator<LiveRef> iter = refs.iterator();
296
while (iter.hasNext()) {
297
LiveRef ref = iter.next();
298
assert ref.getEndpoint().equals(endpoint);
299
300
RefEntry refEntry = refTable.get(ref);
301
if (refEntry == null) {
302
LiveRef refClone = (LiveRef) ref.clone();
303
refEntry = new RefEntry(refClone);
304
refTable.put(refClone, refEntry);
305
if (refsToDirty == null) {
306
refsToDirty = new HashSet<>(5);
307
}
308
refsToDirty.add(refEntry);
309
}
310
311
refEntry.addInstanceToRefSet(ref);
312
}
313
314
if (refsToDirty == null) {
315
return true;
316
}
317
318
refsToDirty.addAll(invalidRefs);
319
invalidRefs.clear();
320
321
sequenceNum = getNextSequenceNum();
322
}
323
324
makeDirtyCall(refsToDirty, sequenceNum);
325
return true;
326
}
327
328
/**
329
* Remove the given RefEntry from the ref table. If that makes
330
* the ref table empty, remove this entry from the global endpoint
331
* table.
332
*
333
* This method must ONLY be called while synchronized on this entry.
334
*/
335
private void removeRefEntry(RefEntry refEntry) {
336
assert Thread.holdsLock(this);
337
assert !removed;
338
assert refTable.containsKey(refEntry.getRef());
339
340
refTable.remove(refEntry.getRef());
341
invalidRefs.remove(refEntry);
342
if (refTable.isEmpty()) {
343
synchronized (endpointTable) {
344
endpointTable.remove(endpoint);
345
Transport transport = endpoint.getOutboundTransport();
346
transport.free(endpoint);
347
/*
348
* If there are no longer any live remote references
349
* registered, we are no longer concerned with the
350
* latency of local garbage collection here.
351
*/
352
if (endpointTable.isEmpty()) {
353
assert gcLatencyRequest != null;
354
gcLatencyRequest.cancel();
355
gcLatencyRequest = null;
356
}
357
removed = true;
358
}
359
}
360
}
361
362
/**
363
* Make a DGC dirty call to this entry's endpoint, for the ObjIDs
364
* corresponding to the given set of refs and with the given
365
* sequence number.
366
*
367
* This method must NOT be called while synchronized on this entry.
368
*/
369
private void makeDirtyCall(Set<RefEntry> refEntries, long sequenceNum) {
370
assert !Thread.holdsLock(this);
371
372
ObjID[] ids;
373
if (refEntries != null) {
374
ids = createObjIDArray(refEntries);
375
} else {
376
ids = emptyObjIDArray;
377
}
378
379
long startTime = System.currentTimeMillis();
380
try {
381
Lease lease =
382
dgc.dirty(ids, sequenceNum, new Lease(vmid, leaseValue));
383
long duration = lease.getValue();
384
385
long newRenewTime = computeRenewTime(startTime, duration);
386
long newExpirationTime = startTime + duration;
387
388
synchronized (this) {
389
dirtyFailures = 0;
390
setRenewTime(newRenewTime);
391
expirationTime = newExpirationTime;
392
}
393
394
} catch (Exception e) {
395
long endTime = System.currentTimeMillis();
396
397
synchronized (this) {
398
dirtyFailures++;
399
400
if (e instanceof UnmarshalException
401
&& e.getCause() instanceof InvalidClassException) {
402
DGCImpl.dgcLog.log(Log.BRIEF, "InvalidClassException exception in DGC dirty call", e);
403
return; // protocol error, do not register these refs
404
}
405
406
if (dirtyFailures == 1) {
407
/*
408
* If this was the first recent failed dirty call,
409
* reschedule another one immediately, in case there
410
* was just a transient network problem, and remember
411
* the start time and duration of this attempt for
412
* future calculations of the delays between retries.
413
*/
414
dirtyFailureStartTime = startTime;
415
dirtyFailureDuration = endTime - startTime;
416
setRenewTime(endTime);
417
} else {
418
/*
419
* For each successive failed dirty call, wait for a
420
* (binary) exponentially increasing delay before
421
* retrying, to avoid network congestion.
422
*/
423
int n = dirtyFailures - 2;
424
if (n == 0) {
425
/*
426
* Calculate the initial retry delay from the
427
* average time elapsed for each of the first
428
* two failed dirty calls. The result must be
429
* at least 1000ms, to prevent a tight loop.
430
*/
431
dirtyFailureDuration =
432
Math.max((dirtyFailureDuration +
433
(endTime - startTime)) >> 1, 1000);
434
}
435
long newRenewTime =
436
endTime + (dirtyFailureDuration << n);
437
438
/*
439
* Continue if the last known held lease has not
440
* expired, or else at least a fixed number of times,
441
* or at least until we've tried for a fixed amount
442
* of time (the default lease value we request).
443
*/
444
if (newRenewTime < expirationTime ||
445
dirtyFailures < dirtyFailureRetries ||
446
newRenewTime < dirtyFailureStartTime + leaseValue)
447
{
448
setRenewTime(newRenewTime);
449
} else {
450
/*
451
* Give up: postpone lease renewals until next
452
* ref is registered for this endpoint.
453
*/
454
setRenewTime(Long.MAX_VALUE);
455
}
456
}
457
458
if (refEntries != null) {
459
/*
460
* Add all of these refs to the set of refs for this
461
* endpoint that may be invalid (this VM may not be in
462
* the server's referenced set), so that we will
463
* attempt to explicitly dirty them again in the
464
* future.
465
*/
466
invalidRefs.addAll(refEntries);
467
468
/*
469
* Record that a dirty call has failed for all of these
470
* refs, so that clean calls for them in the future
471
* will be strong.
472
*/
473
Iterator<RefEntry> iter = refEntries.iterator();
474
while (iter.hasNext()) {
475
RefEntry refEntry = iter.next();
476
refEntry.markDirtyFailed();
477
}
478
}
479
480
/*
481
* If the last known held lease will have expired before
482
* the next renewal, all refs might be invalid.
483
*/
484
if (renewTime >= expirationTime) {
485
invalidRefs.addAll(refTable.values());
486
}
487
}
488
}
489
}
490
491
/**
492
* Set the absolute time at which the lease for this entry should
493
* be renewed.
494
*
495
* This method must ONLY be called while synchronized on this entry.
496
*/
497
private void setRenewTime(long newRenewTime) {
498
assert Thread.holdsLock(this);
499
500
if (newRenewTime < renewTime) {
501
renewTime = newRenewTime;
502
if (interruptible) {
503
AccessController.doPrivileged(
504
new PrivilegedAction<Void>() {
505
public Void run() {
506
renewCleanThread.interrupt();
507
return null;
508
}
509
});
510
}
511
} else {
512
renewTime = newRenewTime;
513
}
514
}
515
516
/**
517
* RenewCleanThread handles the asynchronous client-side DGC activity
518
* for this entry: renewing the leases and making clean calls.
519
*/
520
private class RenewCleanThread implements Runnable {
521
522
public void run() {
523
do {
524
long timeToWait;
525
RefEntry.PhantomLiveRef phantom = null;
526
boolean needRenewal = false;
527
Set<RefEntry> refsToDirty = null;
528
long sequenceNum = Long.MIN_VALUE;
529
530
synchronized (EndpointEntry.this) {
531
/*
532
* Calculate time to block (waiting for phantom
533
* reference notifications). It is the time until the
534
* lease renewal should be done, bounded on the low
535
* end by 1 ms so that the reference queue will always
536
* get processed, and if there are pending clean
537
* requests (remaining because some clean calls
538
* failed), bounded on the high end by the maximum
539
* clean call retry interval.
540
*/
541
long timeUntilRenew =
542
renewTime - System.currentTimeMillis();
543
timeToWait = Math.max(timeUntilRenew, 1);
544
if (!pendingCleans.isEmpty()) {
545
timeToWait = Math.min(timeToWait, cleanInterval);
546
}
547
548
/*
549
* Set flag indicating that it is OK to interrupt this
550
* thread now, such as if a earlier lease renewal time
551
* is set, because we are only going to be blocking
552
* and can deal with interrupts.
553
*/
554
interruptible = true;
555
}
556
557
try {
558
/*
559
* Wait for the duration calculated above for any of
560
* our phantom references to be enqueued.
561
*/
562
phantom = (RefEntry.PhantomLiveRef)
563
refQueue.remove(timeToWait);
564
} catch (InterruptedException e) {
565
}
566
567
synchronized (EndpointEntry.this) {
568
/*
569
* Set flag indicating that it is NOT OK to interrupt
570
* this thread now, because we may be undertaking I/O
571
* operations that should not be interrupted (and we
572
* will not be blocking arbitrarily).
573
*/
574
interruptible = false;
575
Thread.interrupted(); // clear interrupted state
576
577
/*
578
* If there was a phantom reference enqueued, process
579
* it and all the rest on the queue, generating
580
* clean requests as necessary.
581
*/
582
if (phantom != null) {
583
processPhantomRefs(phantom);
584
}
585
586
/*
587
* Check if it is time to renew this entry's lease.
588
*/
589
long currentTime = System.currentTimeMillis();
590
if (currentTime > renewTime) {
591
needRenewal = true;
592
if (!invalidRefs.isEmpty()) {
593
refsToDirty = invalidRefs;
594
invalidRefs = new HashSet<>(5);
595
}
596
sequenceNum = getNextSequenceNum();
597
}
598
}
599
600
boolean needRenewal_ = needRenewal;
601
Set<RefEntry> refsToDirty_ = refsToDirty;
602
long sequenceNum_ = sequenceNum;
603
AccessController.doPrivileged(new PrivilegedAction<Void>() {
604
public Void run() {
605
if (needRenewal_) {
606
makeDirtyCall(refsToDirty_, sequenceNum_);
607
}
608
609
if (!pendingCleans.isEmpty()) {
610
makeCleanCalls();
611
}
612
return null;
613
}}, SOCKET_ACC);
614
} while (!removed || !pendingCleans.isEmpty());
615
}
616
}
617
618
/**
619
* Process the notification of the given phantom reference and any
620
* others that are on this entry's reference queue. Each phantom
621
* reference is removed from its RefEntry's ref set. All ref
622
* entries that have no more registered instances are collected
623
* into up to two batched clean call requests: one for refs
624
* requiring a "strong" clean call, and one for the rest.
625
*
626
* This method must ONLY be called while synchronized on this entry.
627
*/
628
private void processPhantomRefs(RefEntry.PhantomLiveRef phantom) {
629
assert Thread.holdsLock(this);
630
631
Set<RefEntry> strongCleans = null;
632
Set<RefEntry> normalCleans = null;
633
634
do {
635
RefEntry refEntry = phantom.getRefEntry();
636
refEntry.removeInstanceFromRefSet(phantom);
637
if (refEntry.isRefSetEmpty()) {
638
if (refEntry.hasDirtyFailed()) {
639
if (strongCleans == null) {
640
strongCleans = new HashSet<>(5);
641
}
642
strongCleans.add(refEntry);
643
} else {
644
if (normalCleans == null) {
645
normalCleans = new HashSet<>(5);
646
}
647
normalCleans.add(refEntry);
648
}
649
removeRefEntry(refEntry);
650
}
651
} while ((phantom =
652
(RefEntry.PhantomLiveRef) refQueue.poll()) != null);
653
654
if (strongCleans != null) {
655
pendingCleans.add(
656
new CleanRequest(createObjIDArray(strongCleans),
657
getNextSequenceNum(), true));
658
}
659
if (normalCleans != null) {
660
pendingCleans.add(
661
new CleanRequest(createObjIDArray(normalCleans),
662
getNextSequenceNum(), false));
663
}
664
}
665
666
/**
667
* CleanRequest holds the data for the parameters of a clean call
668
* that needs to be made.
669
*/
670
private static class CleanRequest {
671
672
final ObjID[] objIDs;
673
final long sequenceNum;
674
final boolean strong;
675
676
/** how many times this request has failed */
677
int failures = 0;
678
679
CleanRequest(ObjID[] objIDs, long sequenceNum, boolean strong) {
680
this.objIDs = objIDs;
681
this.sequenceNum = sequenceNum;
682
this.strong = strong;
683
}
684
}
685
686
/**
687
* Make all of the clean calls described by the clean requests in
688
* this entry's set of "pending cleans". Clean requests for clean
689
* calls that succeed are removed from the "pending cleans" set.
690
*
691
* This method must NOT be called while synchronized on this entry.
692
*/
693
private void makeCleanCalls() {
694
assert !Thread.holdsLock(this);
695
696
Iterator<CleanRequest> iter = pendingCleans.iterator();
697
while (iter.hasNext()) {
698
CleanRequest request = iter.next();
699
try {
700
dgc.clean(request.objIDs, request.sequenceNum, vmid,
701
request.strong);
702
iter.remove();
703
} catch (Exception e) {
704
/*
705
* Many types of exceptions here could have been
706
* caused by a transient failure, so try again a
707
* few times, but not forever.
708
*/
709
if (++request.failures >= cleanFailureRetries) {
710
iter.remove();
711
}
712
}
713
}
714
}
715
716
/**
717
* Create an array of ObjIDs (needed for the DGC remote calls)
718
* from the ids in the given set of refs.
719
*/
720
private static ObjID[] createObjIDArray(Set<RefEntry> refEntries) {
721
ObjID[] ids = new ObjID[refEntries.size()];
722
Iterator<RefEntry> iter = refEntries.iterator();
723
for (int i = 0; i < ids.length; i++) {
724
ids[i] = iter.next().getRef().getObjID();
725
}
726
return ids;
727
}
728
729
/**
730
* RefEntry encapsulates the client-side DGC information specific
731
* to a particular LiveRef value. In particular, it contains a
732
* set of phantom references to all of the instances of the LiveRef
733
* value registered in the system (but not garbage collected
734
* locally).
735
*/
736
private class RefEntry {
737
738
/** LiveRef value for this entry (not a registered instance) */
739
private LiveRef ref;
740
/** set of phantom references to registered instances */
741
private Set<PhantomLiveRef> refSet = new HashSet<>(5);
742
/** true if a dirty call containing this ref has failed */
743
private boolean dirtyFailed = false;
744
745
public RefEntry(LiveRef ref) {
746
this.ref = ref;
747
}
748
749
/**
750
* Return the LiveRef value for this entry (not a registered
751
* instance).
752
*/
753
public LiveRef getRef() {
754
return ref;
755
}
756
757
/**
758
* Add a LiveRef to the set of registered instances for this entry.
759
*
760
* This method must ONLY be invoked while synchronized on this
761
* RefEntry's EndpointEntry.
762
*/
763
public void addInstanceToRefSet(LiveRef ref) {
764
assert Thread.holdsLock(EndpointEntry.this);
765
assert ref.equals(this.ref);
766
767
/*
768
* Only keep a phantom reference to the registered instance,
769
* so that it can be garbage collected normally (and we can be
770
* notified when that happens).
771
*/
772
refSet.add(new PhantomLiveRef(ref));
773
}
774
775
/**
776
* Remove a PhantomLiveRef from the set of registered instances.
777
*
778
* This method must ONLY be invoked while synchronized on this
779
* RefEntry's EndpointEntry.
780
*/
781
public void removeInstanceFromRefSet(PhantomLiveRef phantom) {
782
assert Thread.holdsLock(EndpointEntry.this);
783
assert refSet.contains(phantom);
784
refSet.remove(phantom);
785
}
786
787
/**
788
* Return true if there are no registered LiveRef instances for
789
* this entry still reachable in this VM.
790
*
791
* This method must ONLY be invoked while synchronized on this
792
* RefEntry's EndpointEntry.
793
*/
794
public boolean isRefSetEmpty() {
795
assert Thread.holdsLock(EndpointEntry.this);
796
return refSet.size() == 0;
797
}
798
799
/**
800
* Record that a dirty call that explicitly contained this
801
* entry's ref has failed.
802
*
803
* This method must ONLY be invoked while synchronized on this
804
* RefEntry's EndpointEntry.
805
*/
806
public void markDirtyFailed() {
807
assert Thread.holdsLock(EndpointEntry.this);
808
dirtyFailed = true;
809
}
810
811
/**
812
* Return true if a dirty call that explicitly contained this
813
* entry's ref has failed (and therefore a clean call for this
814
* ref needs to be marked "strong").
815
*
816
* This method must ONLY be invoked while synchronized on this
817
* RefEntry's EndpointEntry.
818
*/
819
public boolean hasDirtyFailed() {
820
assert Thread.holdsLock(EndpointEntry.this);
821
return dirtyFailed;
822
}
823
824
/**
825
* PhantomLiveRef is a PhantomReference to a LiveRef instance,
826
* used to detect when the LiveRef becomes permanently
827
* unreachable in this VM.
828
*/
829
private class PhantomLiveRef extends PhantomReference<LiveRef> {
830
831
public PhantomLiveRef(LiveRef ref) {
832
super(ref, EndpointEntry.this.refQueue);
833
}
834
835
public RefEntry getRefEntry() {
836
return RefEntry.this;
837
}
838
}
839
}
840
}
841
}
842
843