-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Weak reference processing #564
Conversation
* references: Vec<ObjectReference> * remove unforwarded reference * avoid duplicate references in the queue * forward all references * do not clear references after GC * references: Vec<ObjectReference> -> HashSet<ObjectReference>
reference scanning. Retain soft reference.
f0a3f0d
to
3d29846
Compare
reference: ObjectReference, | ||
tls: VMWorkerThread, | ||
) -> ObjectReference; | ||
/// For reference types, if the referent is cleared during GC, the reference |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK for now because we currently implement Java Reference
semantics. But the term "reference" and "reference type" is confusing for both Java and other languages, too, mostly other languages.
We already use the term "reference" to refer to a pointer to an object. Here we refer to a subclass of java.lang.ref.Reference
.
In Java, some textbooks use the term "primitive types" (or "value types") to refer to bool
, char
, byte
, short
, int
, long
, float
and double
, and use the term "reference types" to refer to java.lang.Object
and all of its descendants. Other languages may use this term similarly.
We should find a more generally acceptable term for "reference type" and "reference processor" and so on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel we can use the word 'weak reference' as a general term to refer to all kinds of weak references (including soft/phantom references). Also we can use WeakReferenceGlue
, and WeakReferenceProcessor
.
src/util/reference_processor.rs
Outdated
if !referent.is_null() { | ||
trace.retain_referent(referent); | ||
Self::keep_referent_alive(trace, referent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keep_referent_alive
calls e.trace_object
, and it may move the object, and return the forwarded reference. But we are not calling VMReferenceGlue::set_referent
here. Currently the two call sites of set_referent
are:
- in
forward
for MarkCompact. - in
process_reference
. Butscan
callsprocess_reference
whenretain
isfalse
. But this line is executed whenretain
istrue
.
So if we use the SemiSpace
plan, and a SoftReference
holds the only pointer to another object, and this is the first time this edge is traced, will this SoftReference
hold a dangling pointer after that?
Self::keep_referent_alive(trace, referent); | |
let new_referent = Self::keep_referent_alive(trace, referent); | |
<E::VM as VMBinding>::VMReferenceGlue::set_referent(reference, new_referent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is not necessary. However, the code is a bit confusing. What actually happens is this:
- In
SoftRefClosure
:- if emergency collection { retain soft refs (no update) }
- In
WeakRefClosure
:- scan soft refs (with update)
- scan weak refs (with update)
I will change this to:
- In
SoftRefClosure
:- if emergency collection { retain soft refs (no update) }
- scan soft refs (with update)
- In
WeakRefClosure
:- scan weak refs (with update)
binding-refs |
Performance results for the PR (Immix) Benchmark time: 1% slowdown with weak ref processing STW time: 10% slowdown with weak ref Immix process edges time: 5% slowdown with weak ref Data link (not publicly visible)link |
The following program crashes on OpenJDK with MMTk, but runs fine without import java.lang.ref.SoftReference;
class Foo {
public SoftReference<Foo> sr;
int num;
}
public class SoftStrongSoft {
public static SoftReference<Foo> setUp() {
Foo foo1 = new Foo();
foo1.num = 30;
Foo foo2 = new Foo();
foo2.num = 40;
SoftReference<Foo> sr1 = new SoftReference<Foo>(foo1);
SoftReference<Foo> sr2 = new SoftReference<Foo>(foo2);
foo1.sr = sr2;
return sr1;
}
public static void fillUpTheHeap() {
String[] ar = new String[1000000];
for (int i = 1; i < 1000000; i++) {
String str = String.valueOf(i);
if (i % 100 == 0) {
ar[i] = str;
}
}
}
public static void main(String[] args) {
SoftReference<Foo> sr1 = setUp();
fillUpTheHeap();
//System.gc();
//Runtime.getRuntime().gc(); // Just in case System.gc doesn't trigger GC.
Foo foo1 = sr1.get();
Foo foo2 = foo1.sr.get();
System.out.println(foo1.num);
System.out.println(foo2.num);
}
} Run with: javac SoftStrongSoft.java -source 11 -target 11
export MMTK_NO_REFERENCE_TYPES=false
/path/to/java -XX:+UseThirdPartyHeap -Xmx64M SoftStrongSoft Result:
The first I think we should repeat the soft reference processing until no further objects can be reached by traversing soft-strong-soft-srong-soft-...-soft-strong references. FinalizerReference will need it, too. It should not be needed for weak/phantom references because weak/phantom references never keep their referents alive. |
On second thought, I feel there is no guarantee that soft references in this example will be retained, and it is not a correctness bug if the soft references are not retained. It probably will depend on the GC implementation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I observed the same behaviour in JikesRVM. The Java API documentation seems to allow this behaviour for SoftReference.
This PR looks good now.
This PR introduces weak reference, but weak reference processing is disabled by default. The following results show that with disabled weak reference processing, the GC/STW time is unchanged, in comparison with I will merge this PR soon. |
This adds weak reference support, and updates MMTk core to mmtk/mmtk-core#564. * if weak reference is enabled in MMTk, we will add weak references during our tracing. Otherwise, still treat them as strong references. * implement new changes in mmtk/mmtk-core#564 * add test with weak reference enabled (by default, it is disabled).
This adds weak reference support, and updates MMTk core to mmtk/mmtk-core#564. * implement new changes in mmtk/mmtk-core#564 * properly set `referent` in `add_xxxx_candidates()`. * Implement `get_boolean_option()` (see changes in mmtk/jikesrvm#12). * add test with weak reference enabled (by default, it is disabled).
This PR revamps the reference processor, and schedules reference processing work. This PR is one step towards #544: it gets the old reference processor working again.
There is some more work needed for weak reference processing. So
no_reference_types
istrue
and by default, we do not process weak references.Changes
ReferenceProcessor
:HashSet
rather thanVec
. We used to assume weak references are added to MMTk once they are created, so for one reference, it will be added once. Now we allow adding weak references during GC, and a weak reference may be traced multiple times in a GC, and added multiple times to MMTk. UsingHashSet
can deduplicate the references.UnsafeCell
forReferenceProcessorSync
.unforwarded_references
: I suspect it was intended to deal with meta-circularity in JikesRVM/Java MMTk (https://github.com/JikesRVM/JikesRVM/blob/5072f19761115d987b6ee162f49a03522d36c697/MMTk/ext/vm/jikesrvm/org/jikesrvm/mm/mmtk/ReferenceProcessor.java#L96), and is no longer needed for MMTk core. For MMTk core, any Rust struct and field is 'untraced' unless we specifically 'trace' it.allow_new_candidate
field to avoid adding new candidates after we finish processing references. See the comments for the field for details.process_reference()
is moved fromReferenceGlue
(in VMBinding traits) toReferenceProcessor
.enqueue_references()
is added toReferenceGlue
.SoftRefClosure
,WeakRefClosure
,PhantomRefClosure
,FinalRefClosure
,FinalizableForwarding
.ProcessWeakRef
(which callVMBinding::VMCollection::process_weak_refs()
) toVMProcessWeakRef
.add_soft/weak/phantom_candidate()
now no longer needs areferent
argument.