Below is a Java code that proves that GC is delayed by using a SoftReference, in other words, GC will only happen if your application comes to a point that it is about to crashunder memory pressure, with the risk of crashing with an OutOfMemoryError. That's exactly the feature we want for our pool.
Below is a Java code that proves that GC is delayed by using a SoftReference, in other words, GC will only happen if your application comes to a point that it is about to crash with an OutOfMemoryError. That's exactly the feature we want for our pool.
Below is a Java code that proves that GC is delayed by using a SoftReference, in other words, GC will only happen if your application comes under memory pressure, with the risk of crashing with an OutOfMemoryError. That's exactly the feature we want for our pool.
Also note that you are not obligated to use a SoftReference. You can use a MultiArrayObjectPool or a TieredObjectPool which are pool implementations that do not discard arrays when they grow. Another important thing to note is that the pool, with a good enough initialCapacity and growthFactor, will grow very few times. Therefore it would also be perfectly fine to just hold the strong reference of the discarded array without wrapping it around a SoftReference, in this case really avoiding the GC forever, even when a OutOfMemoryError is about to crash your application.
I think the confusion lies in that fact that an object pool is not an object cache. They are two very different things. An object cache wants as much memory as possible, to cache as much as possible. An object pool just wants to reuse instances to avoid garbage. They have completely different purpose. In that sense, of an object pool and not an object cache, an OutOfMemoryError usually means a bug/leak in the application and should never happen.
Also note that you are not obligated to use a SoftReference. You can use a MultiArrayObjectPool or a TieredObjectPool which are pool implementations that do not discard arrays when they grow. Another important thing to note is that the pool, with a good enough initialCapacity and growthFactor, will grow very few times. Therefore it would also be perfectly fine to just hold the strong reference of the discarded array without wrapping it around a SoftReference, in this case really avoiding the GC forever, even when a OutOfMemoryError is about to crash your application.
I think the confusion lies in that fact that an object pool is not an object cache. They are two very different things. An object cache wants as much memory as possible, to cache as much as possible. An object pool just wants to reuse instances to avoid garbage. They have completely different purpose. In that sense, of an object pool and not an object cache, an OutOfMemoryError usually means a bug/leak in the application and should never happen.
Regarding this comment:
Having softly-refed objects doesn't delay a GC; it means that objects which are softly-refed might not be collected if the GC was able to free enough non-refed and weakly-refed objects. The typical use case is for caching the result of an expensive computation - by making the cache a soft-ref, you're telling the JVM that it is okay to throw out the cache (during a GC), if doing so allows you to avoid an OOM error. In your pool you are making the E-arrays garbage when you resize the array. This is a weird approach for a pool which wants "grow without generating garbage"
Below is a Java code that proves that GC is delayed by using a SoftReference, in other words, GC will only happen if your application comes to a point that it is about to crash with an OutOfMemoryError. That's exactly the feature we want for our pool.
import java.lang.ref.SoftReference; import java.util.List; import java.util.ArrayList; public class SoftReferenceTest { static volatile boolean keepAllocating = true; public static class MyObject { private final int index; public final byte[] data = new byte[1024 * 1024]; public MyObject(int index) { this.index = index; } @Override public void finalize() { System.out.println("\nGarbage collected => " + index + " thread=" + Thread.currentThread()); // If EVEN then you are a SoftReference being collected if (index % 2 == 0) keepAllocating = false; // STOP LOOP DOWN BELOW } } public static void main(String[] args) throws InterruptedException { List<SoftReference<MyObject>> oldObjects = new ArrayList<>(); for(int i = 0; i < 4; i++) { MyObject myObject = new MyObject(i); if (i % 2 == 0) { // only add EVEN objects as SoftReferences oldObjects.add(new SoftReference<MyObject>(myObject)); } // ODD objects will go out-of-scope and be collected by the gc } // Force the GC and notice that the even objects are NOT collected System.out.println("Forcing the GC..."); System.gc(); Thread.sleep(100); // Now start allocating memory like crazy to force the // SoftReference to be collected List<byte[]> list = new ArrayList<>(1024); int counter = 0; int totalMemory = 0; int memoryToAllocate = 1024 * 128; System.out.println(); while(keepAllocating) { list.add(new byte[memoryToAllocate]); System.out.print("\r"); System.out.print(counter++); totalMemory += memoryToAllocate; System.out.print(" "); System.out.print(totalMemory); Thread.sleep(10); } } } Output:
$ java -Xms64m -Xmx64m -cp . SoftReferenceTest Forcing the GC... Garbage collected => 3 thread=Thread[#10,Finalizer,8,system] Garbage collected => 1 thread=Thread[#10,Finalizer,8,system] 384 50462720 <== App is running but SoftReference is not collected Garbage collected => 2 thread=Thread[#10,Finalizer,8,system] Garbage collected => 0 thread=Thread[#10,Finalizer,8,system] Note that we have successfully avoided the GC until the very end when the JVM was about to run out of memory.
384 50462720 => 50Mb when our max heap size was 64Mb Now run the test again, but this time with a larger max heap size:
$ java -Xms128m -Xmx128m -cp . SoftReferenceTest Forcing the GC... Garbage collected => 1 thread=Thread[#10,Finalizer,8,system] Garbage collected => 3 thread=Thread[#10,Finalizer,8,system] 847 111149056 <== App is running but SoftReference is not collected Garbage collected => 0 thread=Thread[#10,Finalizer,8,system] Garbage collected => 2 thread=Thread[#10,Finalizer,8,system] This time, because the JVM had more memory, it waited to collect the SoftReference until 111Mb of memory were allocated instead of only 50Mb.
847 111149056 => 111Mb when our max heap size was 128Mb That proves that a SoftReference is delayed until the JVM is about to run out of memory.
Note that if your application comes to a point that it is running out of memory and it is about to crash, of course it is much better to GC and try to avoid the OOM crash. That's exactly the purpose of the SoftReference and that's why we choose to use it for the pool.
Regarding this comment:
Having softly-refed objects doesn't delay a GC; it means that objects which are softly-refed might not be collected if the GC was able to free enough non-refed and weakly-refed objects. The typical use case is for caching the result of an expensive computation - by making the cache a soft-ref, you're telling the JVM that it is okay to throw out the cache (during a GC), if doing so allows you to avoid an OOM error. In your pool you are making the E-arrays garbage when you resize the array. This is a weird approach for a pool which wants "grow without generating garbage"
Below is a Java code that proves that GC is delayed by using a SoftReference, in other words, GC will only happen if your application comes to a point that it is about to crash with an OutOfMemoryError. That's exactly the feature we want for our pool.
import java.lang.ref.SoftReference; import java.util.List; import java.util.ArrayList; public class SoftReferenceTest { static volatile boolean keepAllocating = true; public static class MyObject { private final int index; public final byte[] data = new byte[1024 * 1024]; public MyObject(int index) { this.index = index; } @Override public void finalize() { System.out.println("\nGarbage collected => " + index + " thread=" + Thread.currentThread()); // If EVEN then you are a SoftReference being collected if (index % 2 == 0) keepAllocating = false; // STOP LOOP DOWN BELOW } } public static void main(String[] args) throws InterruptedException { List<SoftReference<MyObject>> oldObjects = new ArrayList<>(); for(int i = 0; i < 4; i++) { MyObject myObject = new MyObject(i); if (i % 2 == 0) { // only add EVEN objects as SoftReferences oldObjects.add(new SoftReference<MyObject>(myObject)); } // ODD objects will go out-of-scope and be collected by the gc } // Force the GC and notice that the even objects are NOT collected System.out.println("Forcing the GC..."); System.gc(); Thread.sleep(100); // Now start allocating memory like crazy to force the // SoftReference to be collected List<byte[]> list = new ArrayList<>(1024); int counter = 0; int totalMemory = 0; int memoryToAllocate = 1024 * 128; System.out.println(); while(keepAllocating) { list.add(new byte[memoryToAllocate]); System.out.print("\r"); System.out.print(counter++); totalMemory += memoryToAllocate; System.out.print(" "); System.out.print(totalMemory); Thread.sleep(10); } } } Output:
$ java -Xms64m -Xmx64m -cp . SoftReferenceTest Forcing the GC... Garbage collected => 3 thread=Thread[#10,Finalizer,8,system] Garbage collected => 1 thread=Thread[#10,Finalizer,8,system] 384 50462720 <== App is running but SoftReference is not collected Garbage collected => 2 thread=Thread[#10,Finalizer,8,system] Garbage collected => 0 thread=Thread[#10,Finalizer,8,system] Note that we have successfully avoided the GC until the very end when the JVM was about to run out of memory.
384 50462720 => 50Mb when our max heap size was 64Mb Now run the test again, but this time with a larger max heap size:
$ java -Xms128m -Xmx128m -cp . SoftReferenceTest Forcing the GC... Garbage collected => 1 thread=Thread[#10,Finalizer,8,system] Garbage collected => 3 thread=Thread[#10,Finalizer,8,system] 847 111149056 <== App is running but SoftReference is not collected Garbage collected => 0 thread=Thread[#10,Finalizer,8,system] Garbage collected => 2 thread=Thread[#10,Finalizer,8,system] This time, because the JVM had more memory, it waited to collect the SoftReference until 111Mb of memory were allocated instead of only 50Mb.
847 111149056 => 111Mb when our max heap size was 128Mb That proves that a SoftReference is delayed until the JVM is about to run out of memory.
Note that if your application comes to a point that it is running out of memory and it is about to crash, of course it is much better to GC and try to avoid the OOM crash. That's exactly the purpose of the SoftReference and that's why we choose to use it for the pool.