Long chains created by direct Buffer::slice

mark.reinhold at oracle.com mark.reinhold at oracle.com
Fri Jul 20 18:15:59 UTC 2018


2018/7/20 10:55:52 -0700, Florian Weimer <fw at deneb.enyo.de>:
> ...
> 
> The constructor invoked:
> 
> |     // For duplicates and slices
> |     //
> |     Direct$Type$Buffer$RW$$BO$(DirectBuffer db,         // package-private
> |                                int mark, int pos, int lim, int cap,
> |                                int off)
> |     {
> | #if[rw]
> |         super(mark, pos, lim, cap);
> |         address = db.address() + off;
> | #if[byte]
> |         cleaner = null;
> | #end[byte]
> |         att = db;
> | #else[rw]
> |         super(db, mark, pos, lim, cap, off);
> |         this.isReadOnly = true;
> | #end[rw]
> |     }
> 
> The key part is the assignment to the att member.  If I understand
> this correctly, it is needed to keep the backing object alive during
> the lifetime of this buffer.

Correct.

>                               However, it causes the creation of a
> long chain of buffer objects.  With -Xmx100m or so, the following test
> will OOM fairly quickly for this reason:
> 
> |   volatile ByteBuffer buffer;
> |   …
> |   buffer = ByteBuffer.allocateDirect(16384);
> |   while (true) {
> |       buffer = buffer.duplicate();
> |   }

Well spotted!  This bug has been lurking there for sixteen years.

> 
> I wonder if it would be possible to change the setting of the att
> member to this instead:
> 
> |         if (db.att == null) {
> |             att = db;
> |         } else {
> |             att = db.att;
> |         }
> 
> This would only keep the object alive which actually owns the backing
> storage, as if Buffer::slice had been invoked on it directly.

Your suggested fix looks fine.

- Mark


More information about the core-libs-dev mailing list