The Util bytebuffer cache holds three buffers per thread to reuse for IOUtil operations such as read/write for cases where a temporary native buffer is needed. However, the cache fills up with the first three buffers used, and once that occurs, any additional buffers are ignored.
Consider the following case: the three buffers are filled with small buffers, and then a large read operation is initiated in a tight readFully loop. A large buffer is created for the read, and so the three cached buffers are skipped over and a large temp buffer is created. Some small fraction of this large buffer is filled with the read, and the large temp buffer is discarded but since the cache is full it is discarded. Then the next iteration of the readFully loop causes another, slightly smaller buffer to be created, and so on, causing a large number of large temp buffers to be created and discarded, and the cache is no help at all.
This can be rectified by modifying the cache to hold onto the larger buffers rather than the smaller ones.