I have a class whose instances are initialized and used by underlying flatform.
class MyAttributeConverter implements AttributeConverter<XX, YY> {
public YY convertToDatabaseColumn(XX attribute) { return null; }
public XX convertToEntityAttribute(YY dbData) { return null; }
}
Nothing's wrong and I thought I need to add some static methods for being used as method references.
private static MyAttributeConverter instance;
// just a lazy-initialization;
// no synchronization is required;
// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
if (instance == null) {
instance = new MyAttributeConverter();
}
return instance;
}
// do as MyAttributeConverter::toDatabaseColumn(xx)
public static YY toDatabaseColumn(XX attribute) {
return instance().convertToDatabaseColumn(attribute);
}
public static XX toEntityAttribute(YY dbData) {
return instance().convertToEntityAttribute(attribute);
}
Still nothing seems wrong (I believe) and I don't like the instance persisted with the class and that's why I'm trying to do this.
private static WeakReference<MyAttributeConverter> reference;
public static <R> R applyInstance(Function<? super MyAttributeConverter, ? extends R> function) {
MyAttributeConverter referent;
if (reference == null) {
referent = new MyAttributeConverter();
refernce = new WeakReference<>(referent);
return applyInstance(function);
}
referent = reference.get();
if (referent == null) {
referent = new MyAttributeConverter();
refernce = new WeakReference<>(referent);
return applyInstance(function);
}
return function.apply(referent); // @@?
}
I basically don't even know how to test this code. And I'm sorry for my questions which each might be somewhat vague.
- Is this a (right/wrong) approach?
- Is there any chance that
reference.get()inside thefunction.applyidiom may benull? - Is there any chance that there may be some problems such as memory-leak?
- Should I rely on
SoftReferencerather thanWeakReference?
Thank you.
Note that a method like
is not thread safe, as it bears two reads of the
instancefield; each of them may perceive updates made by other threads or not. This implies that the first read ininstance == nullmay perceive a newer value written by another thread whereas the second inreturn instance;could evaluate to the previous value, i.e.null. So this method could returnnullwhen more than one thread is executing it concurrently. This is a rare corner case, still, this method is not safe. You’d need a local variable to ensure that the test and the return statement use the same value.This still is only safe when
MyAttributeConverteris immutable using onlyfinalfields. Otherwise, a thread may return an instance created by another thread in an incompletely constructed state.You can use the simple way to make it safe without those constraints:
This still is lazy as class initialization only happens on one of the specified triggers, i.e. the first invocation of the method
instance().Your usage of
WeakReferenceis subject to the same problems. Further, it’s not clear why you resort to a recursive invocation of your method at two points where you already have the required argument in a local variable.A correct implementation can be far simpler:
But before you are going to use it, you should reconsider whether the complicated code is worth the effort. The fact that you are accepting the need to reconstruct the object when it has been garbage collected, even potentially constructing multiple instances on concurrent invocations, suggest that you know that the construction will be cheap. When the construction is cheap, you probably don’t need to cache an instance of it at all.
Just consider
It’s at least worth trying, measuring the application’s performance and comparing it with the other approaches.
On the other hand, it doesn’t look like the instance was occupying a significant amount of memory nor holding non-memory resources. As otherwise, you were more worried about the possibility of multiple instances flying around. So the other variant worth trying and comparing, is the one shown above using a
static finalfield with lazy class initialization and no opportunity to garbage collect that small object.One last clarification. You asked
Since there is no
reference.get()invocation inside the evaluation offunction.apply, there is no chance that such an invocation may evaluate tonullat this point. The function receives a strong reference and since the calling code ensured that this strong reference is notnull, it will never becomenullduring the invocation of theapplymethod.Generally, the garbage collector will never alter the application state in a way that code using strong references will notice a difference (letting the availability of more memory aside).
But since you asked specifically about
reference.get(), a garbage collector may collect an object after its last use, regardless of method executions or local scopes. So the referent could get collected during the execution of theapplymethod when this method does not use the object anymore. Runtime optimizations may allow this to happen earlier than you might guess by looking at the source code, because what may look like an object use (e.g. a field read) may not use the object at runtime (e.g. because that value is already held in a CPU register, eliminating the need to access the object’s memory). As said, all without altering the method’s behavior.So a hypothetical
reference.get()during the execution of theapplymethod could in principle evaluate tonull, but there is no reason for concern, as said, the behavior of theapplymethod does not change. The JVM will retain the object’s memory as long as needed for ensuring this correct method execution.But that explanation was just for completeness. As said, you should not use weak nor soft references for objects not holding expensive resources.