I have faced the problem sometimes my weak reference to some object just disappeared, even if I had the strong reference remembered in the local variable. But it seems to me this can have something to do with the JIT optimization. Today with Java 11 it seems to me I could find the repeatable confirmation of this behavior. So it is possible, that in the case the reference to some content of the weak reference is remembered in the local variable, it may not prevent this object from garbage collection. I could reproduce the problem with following construct:
SomeObjectStructure locId = generateIdAndRememberInternallyWeakReferenceToIdObject();
// This did not work:
// Reference.reachabilityFence(locId);
// The only working solution for my singleton was to assign
// the Id to the ThreadLocal field value of the singleton:
// currentThreadId.set(locId)
try {
// do some stuff here, but do not work with variable locId
// directly, only try to ask for the weak reference sometimes
somethingAskingWeakReferenceToId();
// In the above method sometimes, the weak reference shows null, so
// even if one would think, the local variable locId
// is preventing the object from garbage collection, it does not.
// There were situations where at this time the id has
// already gone. In other cases it was still there.
// This has then serious consequences if you rely
// on the presence of the Id in the weak reference
// container in the code here:
doSomethingOnTheBasisOfContentOfRememberedWeakReference();
} finally {
// Following statement has been intended to release the strong reference
// locId to allow the GC of the the weakly internally remembered Id
// after the finally block ends, but
// sometimes this can be ignored by the compiler
// or optimizer, the variable
// could be released much earlier than after this place in the code:
locId = null;
// Instead of the above statement I used later this one:
// currentThreadId.set(null);
// to release the locId remembered in the ThreadLocal variable
// of class containing the code.
}
I found the page to the Oracle optimizations and similar stuff could be possibly also concluded from it, see here. Am I right, is it so? Or is there some other explanation of this behavior? If I remembered the id additionally into some ThreadLocal variable (my objects are singletons), then the problem disappeared too. Also in this question the similar problem is discussed and the solution is proposed to use
Reference.reachabilityFence(locId);
right after the variable is assigned which I also tested, but in my case this did not help and the variable content still has been garbage collected before reaching the finally block... Why is it so?
But the solution with the fence would have also the drawback, the variable content, if I understood it correctly, should be then remembered not until the visibility scope of the variable ends, but until the method is finished. But it still did not work...
Is there any other way how to prevent this behavior of the compiler/optimizer?