Suspected regression: fix for 6735255 causes delay in GC of ZipFile InputStreams, increase in heap demand

Xueming Shen xueming.shen at
Wed Apr 13 19:22:24 UTC 2011

On 04/13/2011 08:25 AM, Steve Poole wrote:
> On 13/04/11 16:19, Steve Poole wrote:
>> On 12/04/11 20:33, Xueming Shen wrote:
>>> Hi Neil,
>> Hi Sherman ,  Neil is out on vacation so  I will do my best to stand 
>> in for him.
>>> (1) I believe it would be better to keep the synchronization lock 
>>> for get/releaseInfalter()
>>>      "local" instead of using the "global" ZipFile.this, which I 
>>> agree is "simple". But it also
>>>      means each/every time when you release the used inflater back 
>>> to cache, ZipFile.this
>>>      has to be held and any possible/potential read operation on 
>>> ZipFile from other thead/
>>>      inputstream has to wait ("get" seems fine, the current impl 
>>> holds the ZipFile.this
>>>      anyway before reach the getInflater()).
>> Ok - I agree - seems sensible.  (Though I reserve the right to 
>> contradict myself later)
>>> (2) The "resource" Infalter mainly holds is the native memory block 
>>> it alloc-ed at native
>>>      for zlib, which is not in the Java heap, so I doubt making it 
>>> "softly" really helps for GC.
>>>      Sure, cleanup of those "objects" themself is a plus, but I'm 
>>> not sure if it is really worth
>>>      using SoftReference in this case (it appears you are now 
>>> invoking clearStaleStreams()
>>>      from the finalize()).
>> I'd like to keep this in -  not all implementations of zlib are equal 
>> in where they allocate memory.

Hi Steve,

It might be better/appropriate to use the SoftReference in the 
"inflaterCache" instead of the
"streams", if we want to use the SoftReference for inflater caching purpose.

      * Gets an inflater from the list of available inflaters or allocates
      * a new one.
     private synchronized Inflater getInflater() {
         SoftReference<Inflater> sref;
         Inflater inf;
         while (null != (sref = inflaterCache.poll())) {
             if ((inf = sref.get()) != null && !inf.ended()) {
                 return inf;
         return new Inflater(true);

      * Releases the specified inflater to the list of available inflaters.
     private synchronized void releaseInflater(Inflater inf) {
         if ((null != inf) && (false == inf.ended())) {
             inflaterCache.add(new SoftReference(inf));

     // List of available Inflater objects for decompression
     private Deque<SoftReference<Inflater>> inflaterCache = new 


>>> (3) The releaseInfalter() is now totally driven from 
>>> clearStaleStreams()/staleStreamQueue,
>>>      which is under full control of GC. If GC does not kick in for 
>>> hours/days, infalters can never
>>>      be put back into its cache after use, even the applications 
>>> correctly/promptly close those
>>>      streams after use. And when the GC kicks in, we might see 
>>> "bunch" (hundreds, thousands)
>>>      of inflaters get pushed into cache. The approach does solve the 
>>> "timing" issue that got
>>>      us here, but it also now has this "native memory cache" 
>>> mechanism totally under the
>>>      control of/driven by the GC, the java heap management 
>>> mechanism, which might not be
>>>      a preferred scenario. Just wonder if we can have a better 
>>> choice, say with this GC-driven
>>>      as the backup cleanup and meanwhile still have the 
>>> ZFIIS.close() to do something to safely
>>>      put the used inflater back to cache promptly. To put the 
>>> infalter as the value of the "streams"
>>>      map appears to be a good idea/start, now the "infalter" will 
>>> not be targeted for finalized
>>>      until the entry gets cleaned from the map, in which might in 
>>> fact provide us a sort of
>>>      "orderly" (between the "stream" and its "inflater") clearup 
>>> that the GC/finalizer can't
>>>      guarantee. We still have couple days before we hit the 
>>> "deadline" (to get this one in), so it
>>>      might worth taking some time on this direction?
> Dang! - pressed send when I meant save.   I understand your comments - 
> on the face of it I agree with what you're suggesting - let me think 
> it through some more though.
>> What is this "deadline"  you are talking about?
>>> -Sherman
>>> On 04/11/2011 05:15 AM, Neil Richards wrote:
>>>> On Sun, 2011-04-10 at 18:28 +0100, Neil Richards wrote:
>>>>> With releaseInflater() being driven from the cleanup of stale 
>>>>> 'streams'
>>>>> entries, it no longer needs to be called from ZFIIS.close(), so,
>>>>> instead, only Inflater.reset() is called from there (providing the
>>>>> inflater object has not explicitly been ended) so that it releases 
>>>>> the
>>>>> buffer it has been holding.
>>>> Actually, I have a slight change of heart on this aspect.
>>>> Because close() may be driven from a finalize() method in user code 
>>>> (as
>>>> is done in the InflaterFinalizer test case), I believe it is possible
>>>> (albeit unlikely) for an inflater object to have been reclaimed from
>>>> 'streams' (by a call to clearStaleStreams()), put into the inflater
>>>> cache, retrieved from there (by another thread creating an input 
>>>> stream)
>>>> and having started to be used by that other stream at the point at 
>>>> which
>>>> the code in close() is run.
>>>> (This is because weak references will be cleared, and *may* be 
>>>> queued on
>>>> the ReferenceQueue, prior to finalization).
>>>> Because of this, it's not actually entirely safe now to call 
>>>> inf.reset()
>>>> from ZFIIS.close().
>>>> Instead, I have added additional calls to clearStaleStreams() from the
>>>> finalize() methods of both InputStream implementations in the latest
>>>> (hopefully in the meaning of "last") changeset included below.
>>>> As always, please get back to me with any comments, questions or
>>>> suggestions on this,
>>>> Thanks,
>>>> Neil

More information about the core-libs-dev mailing list