How to make the java system release Soft References?

后端 未结 5 1690
慢半拍i
慢半拍i 2020-12-05 11:06

I\'m going to use a SoftReference-based cache (a pretty simple thing by itself). However, I\'ve came across a problem when writing a test for it.

The objective of th

相关标签:
5条回答
  • 2020-12-05 11:24

    Instead of a long running loop (as suggested by nanda), it's probably faster and easier to simply create a huge primitive array to allocate more memory than available to the VM, then catch and ignore the OutOfMemoryError:

        try {
            long[] foo = new long[Integer.MAX_VALUE];
        }
        catch(OutOfMemoryError e) {
            // ignore
        }
    

    This will clear all weak and soft references, unless your VM has more than 16GB heap available.

    0 讨论(0)
  • 2020-12-05 11:30

    This piece of code forces the JVM to flush all SoftReferences. And it's very fast to do.

    It's working better than the Integer.MAX_VALUE approach, since here the JVM really tries to allocate that much memory.

    try {
        Object[] ignored = new Object[(int) Runtime.getRuntime().maxMemory()];
    } catch (OutOfMemoryError e) {
        // Ignore
    }

    I now use this bit of code everywhere I need to unit test code using SoftReferences.

    Update: This approach will indeed work only with less than 2G of max memory.

    Also, one need to be very careful with SoftReferences. It's so easy to keep a hard reference by mistake that will negate the effect of SoftReferences.

    Here is a simple test that shows it working every time on OSX. Would be interested in knowing if JVM's behavior is the same on Linux and Windows.

    
    for (int i = 0; i < 1000; i++) {
        SoftReference<Object> softReference = new SoftReferencelt<Object>(new Object());
        if (null == softReference.get()) {
            throw new IllegalStateException("Reference should NOT be null");
        }
    
        try {
            Object[] ignored = new Object[(int) Runtime.getRuntime().maxMemory()];
        } catch (OutOfMemoryError e) {
            // Ignore
        }
    
        if (null != softReference.get()) {
            throw new IllegalStateException("Reference should be null");
        }
    
        System.out.println("It worked!");
    }
    0 讨论(0)
  • 2020-12-05 11:31

    You could explicitly set the soft reference to null in your test, and as such simulate that the soft reference has been released.

    This avoids any complicated test setup that is memory and garbage collection dependend.

    0 讨论(0)
  • 2020-12-05 11:39
    1. Set the parameter -Xmx to a very small value.
    2. Prepare your soft reference
    3. Create as many object as possible. Ask for the object everytime until it asked the object from server again.

    This is my small test. Modify as your need.

    @Test
    public void testSoftReference() throws Exception {
        Set<Object[]> s = new HashSet<Object[]>();
    
        SoftReference<Object> sr = new SoftReference<Object>(new Object());
    
        int i = 0;
    
        while (true) {
            try {
                s.add(new Object[1000]);
            } catch (OutOfMemoryError e) {
                // ignore
            }
            if (sr.get() == null) {
                System.out.println("Soft reference is cleared. Success!");
                break;
            }
            i++;
            System.out.println("Soft reference is not yet cleared. Iteration " + i);
      }
    }
    
    0 讨论(0)
  • 2020-12-05 11:47

    An improvement that will work for more than 2G max memory. It loops until an OutOfMemory error occurs.

    @Test
    public void shouldNotHoldReferencesToObject() {
        final SoftReference<T> reference = new SoftReference<T>( ... );
    
        // Sanity check
        assertThat(reference.get(), not(equalTo(null)));
    
        // Force an OoM
        try {
            final ArrayList<Object[]> allocations = new ArrayList<Object[]>();
            int size;
            while( (size = Math.min(Math.abs((int)Runtime.getRuntime().freeMemory()),Integer.MAX_VALUE))>0 )
                allocations.add( new Object[size] );
        } catch( OutOfMemoryError e ) {
            // great!
        }
    
        // Verify object has been garbage collected
        assertThat(reference.get(), equalTo(null));
    
    }
    
    0 讨论(0)
提交回复
热议问题