RFR JDK-8185582, Update Zip implementation to use Cleaner, not finalizers

Peter Levart peter.levart at gmail.com
Wed Sep 27 10:36:20 UTC 2017


Hi David,

On 09/27/2017 11:52 AM, David Holmes wrote:
> Hi Peter,
>
> I thought Cleaner was supposed to avoid all these unpredictable 
> reachability races? Otherwise why switch from using a finalizer ??

Unfortunately, there is no hidden magic in Cleaner. It is better than 
finalize() mostly because the clean-up function may be invoked by the 
user, which also de-registers it, allowing GC to collect it rather than 
leaving it to reference-processing pipeline to process it for no reason. 
And of course, it guarantees that the tracked referent can not be 
resurrected as a result of cleanup code execution. What remains 
unchanged with Cleaner is the point in program execution when the 
tracked referent is determined to be phantom-reachable (finalizable) and 
therefore its associated PhantomReference(s) eligible for processing 
(finalize() invoked).

Regards, Peter

>
> David
>
> On 27/09/2017 7:31 PM, Peter Levart wrote:
>> Hi Sherman,
>>
>> At first I checked the Deflater/Inflater changes. I'll come back with 
>> ZipFile later.
>>
>> I think the code is mostly correct, but I have a concern. If clean() 
>> is invoked via Deflater.end(), then the Deflater instance is still 
>> alive and synchronization is necessary as other threads might be 
>> concurrently invoking other methods. If clean() is invoked via 
>> Cleaner thread, then Deflater instance is already phantom reachable 
>> and no thread may be accessing it or be in the middle of executing 
>> any of its instance methods. How is this guaranteed? Critical 
>> Deflater methods synchronize on the zsRef instance, so at least at 
>> the beginning of the synchronized block, the Deflater instance is 
>> strongly reachable. Take for example the following Deflater instance 
>> method:
>>
>>
>>   254     public void setDictionary(byte[] b, int off, int len) {
>>   255         if (b == null) {
>>   256             throw new NullPointerException();
>>   257         }
>>   258         if (off < 0 || len < 0 || off > b.length - len) {
>>   259             throw new ArrayIndexOutOfBoundsException();
>>   260         }
>>   261         synchronized (zsRef) {
>>   262             ensureOpen();
>>   263             setDictionary(zsRef.address(), b, off, len);
>>   264         }
>>   265     }
>>
>>
>> Up to a point where 'this' is dereferenced to obtain the 'zsRef' 
>> value (line 261), the Deflater instance is reachable. But after that, 
>> even ensureOpen() may be inlined and 'this' is not needed any more. 
>> After that point, obtaining zsRef.address() and calling setDictionaly 
>> on the obtained address may be racing with Cleaner thread invoking 
>> ZStreamRef.run():
>>
>>    49     public void run() {
>>    50         long addr = address;
>>    51         address = 0;
>>    52         if (addr != 0) {
>>    53             end.accept(addr);
>>    54         }
>>    55     }
>>
>>
>> Possible program execution is therefore:
>>
>> Thread1:
>>
>> zsRef.address() - returning non-zero address
>>
>> Thread2:
>>
>> ZStreamRef.run() - invoking Deflater.end(address) with non-zero 
>> address via the passed-in lambda.
>>
>> Thread1:
>>
>> setDictionary(<non-zero address from before>, b, off, len)
>>
>>
>> To fix this, you could sprinkle Reference.reachabilityFence(this) at 
>> appropriate places in Deflater, but it is simpler to just add 
>> synchronized modifier to ZStreamRef.run() method. There's no danger 
>> of deadlock(s) since Cleaner ensures the run() method is invoked at 
>> most once.
>>
>> The same reasoning applies to Inflater to as code is similar.
>>
>> A nit (Deflater and similar to Inflater):
>>
>>   187         ZStreamRef ref = new ZStreamRef(init(level, 
>> DEFAULT_STRATEGY, nowrap),
>>   188                                         addr -> end(addr));
>>
>> You could use a method reference there.
>>
>> Regards, Peter
>>
>>
>>
>> On 09/27/2017 08:35 AM, Xueming Shen wrote:
>>> Hi,
>>>
>>> Please help review the change for JDK-8185582.
>>>
>>> issue:    https://bugs.openjdk.java.net/browse/JDK-8185582
>>> webrev: http://cr.openjdk.java.net/~sherman/8185582/webrev/
>>> csr:       https://bugs.openjdk.java.net/browse/JDK-8187485
>>>
>>> The proposed change here is to replace the finalize() cleanup 
>>> mechanism with
>>> the newly introduced java.lang.ref.Cleaner for java.util.zip package 
>>> (Deflater,
>>> Inflater, ZipFile and 
>>> ZipFile.ZipFileInputStrean/ZipFileInflaterInputStream).
>>>
>>> Since it's an "incompatible" change, both behavioral and source 
>>> incompatibility,
>>> to remove the public finalize() from these classes, a corresponding 
>>> CSR is also
>>> created and need review as well.
>>>
>>> Thanks,
>>> Sherman
>>>
>>



More information about the core-libs-dev mailing list