In ThreadLocalBufferAllocator.getBufferAllocator() there is no guarantee that the SoftReference doesn't get cleared between its construction and its later use of bAllocatorRef.get(). Therefore the method can return null. Calling code (e.g. UTF8Reader.<init>() ) does expects to get non-null though and can crash with NPE. public static BufferAllocator getBufferAllocator() { SoftReference<BufferAllocator> bAllocatorRef = tlba.get(); if (bAllocatorRef == null || bAllocatorRef.get() == null) { bAllocatorRef = new SoftReference<>(new BufferAllocator()); tlba.set(bAllocatorRef); } return bAllocatorRef.get(); // <--- returns null, because soft-ref was already cleared } This semi-reliably crashes SPECjvm2008 XML tests with Shenandoah and aggressive mode that does back-to-back cycles. Example fix that keeps return value reachable: public static BufferAllocator getBufferAllocator() { BufferAllocator ba = null; SoftReference<BufferAllocator> bAllocatorRef = tlba.get(); if (bAllocatorRef != null) { ba = bAllocatorRef.get(); } if (ba == null) { ba = new BufferAllocator(); bAllocatorRef = new SoftReference<>(ba); tlba.set(bAllocatorRef); } return ba; }
|