MemoryUsageTracker { public static String forceClearSoftReferences() { return forceClearSoftReferences(1000*1000, Integer.MAX_VALUE); }  void track(Object instance, long bytesUsedByInstance); long getBytesUsed(); static String forceClearSoftReferences(); static String forceClearSoftReferences(long headroom, int maxChunk); static final MemoryUsageTracker SOFT_REFERENCES; }
@Test(groups="Integration") public void testSoftUsageAndClearance() { MemoryUsageSummary initialMemory = new MemoryUsageSummary(); LOG.info("Memory usage at start of test: "+initialMemory); MemoryUsageSummary beforeCollectedMemory = null; List<Maybe<?>> dump = MutableList.of(); for (int i=0; i<1000*1000; i++) { beforeCollectedMemory = new MemoryUsageSummary(); dump.add(Maybe.soft(new byte[1000*1000])); if (containsAbsent(dump)) break; } int cleared = countAbsents(dump); assertTrue(cleared > 0, "No soft references cleared after trying to allocate all available memory"); LOG.info("First soft reference cleared after "+dump.size()+" 1M blocks created; "+cleared+" of them cleared; memory just before collected is "+beforeCollectedMemory); assertUsedMemoryFractionWithinRange(beforeCollectedMemory, Range.closed(0.7, 1.0)); String clearanceResult = MemoryUsageTracker.forceClearSoftReferences(100*1000, 10*1000*1000); LOG.info("Forcing memory eviction: " + clearanceResult); System.gc(); System.gc(); MemoryUsageSummary afterClearedMemory = new MemoryUsageSummary(); double initialUsedFraction = 1.0*initialMemory.used / afterClearedMemory.total; assertUsedMemoryFractionWithinRange(afterClearedMemory, Range.closed(0.0, initialUsedFraction + 0.1)); LOG.info("Final memory usage (after forcing clear, and GC): "+afterClearedMemory); }