{"id":1743,"date":"2018-03-10T20:34:40","date_gmt":"2018-03-11T01:34:40","guid":{"rendered":"http:\/\/unitstep.net\/?p=1743"},"modified":"2018-03-10T20:34:40","modified_gmt":"2018-03-11T01:34:40","slug":"java-phantomreferences-a-better-choice-than-finalize","status":"publish","type":"post","link":"https:\/\/unitstep.net\/blog\/2018\/03\/10\/java-phantomreferences-a-better-choice-than-finalize\/","title":{"rendered":"Java PhantomReferences: A better choice than finalize()"},"content":{"rendered":"
We’ve talked about soft<\/a> and weak<\/a> references, and how they differ from strong references. But what about phantom references? What are they useful for?<\/p>\n Starting with Java 9, the intended usage of PhantomReference objects is to replace any usage of <\/p>\n The basic usage is to create a Firstly, creating a phantom reference does not make the object phantom-reachable. (The same applies for weak references, and soft references) The object must first lose all (strong) references to it, and then sometime after that, the JVM will determine it’s phantom-reachable. <\/p>\n This is why you must register a phantom reference with a “It is possible to create a phantom reference with a null queue, but such a reference is completely useless: Its get method will always return null and, since it does not have a queue, it will never be enqueued.”<\/p><\/blockquote>\n The phantom reference will then be enqueued in the reference queue at the moment the JVM determines the reference object is only phantom-reachable, (that is, it has no strong, soft, nor weak references), and this serves as a notification of the reachability change.<\/p>\n Using a In fact, starting with Java 9, Before Java 9, Java 8 PhantomReference: <\/strong> Java 9 PhantomReference:<\/strong> This means that in Java 9, PhantomReference objects are dequeued at a later change (from pre-mortem to post-mortem) and the PhantomReference itself<\/strong> should not prevent garbage collection of the object, and thus create a resource leak. Previously, this was not the case<\/a>.<\/p>\n Let’s take a look at a simple and contrived example, my favourite kind of example. (An example is also at: https:\/\/ideone.com\/I8f4U3<\/a>)<\/p>\n In this straightforward example, we:<\/p>\n 1. Create an object.Object.finalize()<\/code> (which was deprecated in Java 9), in order to allow for this sort of object clean-up code to be run in a more predicable manner (as designated by the programmer), rather than subject to the constraints\/implementation details of the garbage collector.<\/p>\n
How to use them<\/h2>\n
PhantomReference<\/a><\/code> that wraps some object reference. However, the
get()<\/code> method will always return
null<\/code> for a
PhantomReference<\/code>, so what can this object even be used for?<\/p>\n
ReferenceQueue<\/a><\/code> in order for it to be useful. This is indicated by the constructor signature, and the accompanying Javadoc:<\/p>\n
Alternative to
finalize<\/code><\/h2>\n
PhantomReference<\/code> along with a
ReferenceQueue<\/code> can allow you to be notified when an object has been finalized by the GC, and thus allow you to perform any necessary clean-up action.<\/p>\n
Object.finalize()<\/code> has been deprecated<\/a>, recognizing what many Java developers have know for some time: That implementing finalize() can lead to error-prone code: The thread which calls finalize() is the garbage-collector thread, which introduces concurrency concerns, and an improper finalize() method could leak a reference to the object itself, preventing it from being GC’d.<\/p>\n
PhantomReference<\/code> didn’t fully address all of these concerns, but there were changes made to bridge this gap. Check out the notable differences in the Java 8<\/a> vs Java 9<\/a> docs:<\/p>\n
\n– Phantom references are most often used for scheduling pre-mortem<\/strong> cleanup actions in a more flexible way than is possible with the Java finalization mechanism.
\n– The Javadoc states: “Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.”<\/em><\/p>\n
\n– Phantom references are most often used to schedule post-mortem<\/strong> cleanup actions.
\n– There’s no mention of them not<\/strong> being automatically cleared by the garbage collector.
\n– The Javadoc states: “Suppose the garbage collector determines at a certain point in time that an object is phantom reachable. At that time it will atomically clear all phantom references to that object and all phantom references to any other phantom-reachable objects from which that object is reachable. At the same time or at some later time it will enqueue those newly-cleared phantom references that are registered with reference queues.”<\/em><\/p>\nA simple example<\/h2>\n
package net.unitstep.examples.references;\r\n\r\nimport java.lang.ref.PhantomReference;\r\nimport java.lang.ref.ReferenceQueue;\r\n\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\n\r\n\/**\r\n * @author Peter Chng\r\n *\/\r\npublic class PhantomReferenceExample {\r\n private static final Logger LOGGER = LoggerFactory.getLogger(PhantomReferenceExample.class);\r\n\r\n \/\/ Just so we have a non-primitive, non-interned object that will be GC'd.\r\n public static class SampleObject<T> {\r\n private final T value;\r\n\r\n public SampleObject(final T value) {\r\n this.value = value;\r\n }\r\n\r\n @Override\r\n public String toString() {\r\n return String.valueOf(this.value);\r\n }\r\n }\r\n\r\n public static class PhantomReferenceMetadata<T, M> extends PhantomReference<T> {\r\n \/\/ Some metadata stored about the object that will be used during some cleanup actions.\r\n private final M metadata;\r\n\r\n public PhantomReferenceMetadata(final T referent, final ReferenceQueue<? super T> q,\r\n final M metadata) {\r\n super(referent, q);\r\n this.metadata = metadata;\r\n }\r\n\r\n public M getMetadata() {\r\n return this.metadata;\r\n }\r\n }\r\n\r\n public static void main(final String[] args) {\r\n \/\/ The object whose GC lifecycle we want to track.\r\n SampleObject<String> helloObject = new SampleObject<>(\"Hello\");\r\n\r\n \/\/ Reference queue that the phantom references will be registered to.\r\n \/\/ They will be enqueued here when the appropriate reachability changes are detected by the JVM.\r\n final ReferenceQueue<SampleObject<String>> refQueue = new ReferenceQueue<>();\r\n\r\n \/\/ In this case, the metadata we associate with the object is some name.\r\n final PhantomReferenceMetadata<SampleObject<String>, String> helloPhantomReference = new PhantomReferenceMetadata<>(\r\n helloObject, refQueue, \"helloObject\");\r\n\r\n new Thread(() -> {\r\n LOGGER.info(\"Starting ReferenceQueue consumer thread.\");\r\n final int numToDequeue = 1;\r\n int numDequed = 0;\r\n while (numDequed < numToDequeue) {\r\n \/\/ Unfortunately, need to downcast to the appropriate type.\r\n try {\r\n @SuppressWarnings(\"unchecked\")\r\n final PhantomReferenceMetadata<SampleObject<String>, String> reference = (PhantomReferenceMetadata<SampleObject<String>, String>) refQueue\r\n .remove();\r\n\r\n \/\/ At this point, we know the object referred to by the PhantomReference has been finalized.\r\n \/\/ So, we can do any other clean-up that might be allowed, such as cleaning up some temporary files\r\n \/\/ associated with the object.\r\n \/\/ The metadata stored in PhantomReferenceMetadata could be used to determine which temporary files\r\n \/\/ should be cleaned up.\r\n \/\/ You probably shouldn't rely on this as the ONLY method to clean up those temporary files, however.\r\n LOGGER.info(\"{} has been finalized.\", reference.getMetadata());\r\n } catch (final InterruptedException e) {\r\n \/\/ Just for the purpose of this example.\r\n break;\r\n }\r\n ++numDequed;\r\n }\r\n LOGGER.info(\"Finished ReferenceQueue consumer thread.\");\r\n }).start();\r\n\r\n \/\/ Lose the strong reference to the object.\r\n helloObject = null;\r\n\r\n \/\/ Attempt to trigger a GC.\r\n System.gc();\r\n }\r\n}<\/code><\/pre>\n
\n2. Create a ReferenceQueue<\/code> for the JVM to use.
\n3. Wrap that object in a PhantomReference<\/code>-derived class, attach some metadata to it, and register it with the <\/code>ReferenceQueue<\/code>
\n4. When the JVM detects the appropriate reachability changes (i.e, there’s no longer a strong reference to helloObject<\/code>, and it’s only phantom-reachable), it will enqueue the phantom reference object into the reference queue.
\n5. We create a separate thread to monitor the reference queue, and could do some clean-up associated with the object here.<\/p>\n