Как заставить систему java выпускать Soft Reference?

Я собираюсь использовать кеш SoftReference (довольно простая вещь сама по себе). Тем не менее, я столкнулся с проблемой при написании теста для него.

Цель теста - проверить, запрашивает ли кеш предыдущий кешированный объект с сервера после очистки памяти.

Здесь я нахожу проблему, как заставить систему выпускать мягкие ссылочные объекты. Вызов System.gc() недостаточно, потому что мягкие ссылки не будут выпущены до тех пор, пока память не станет низкой. Я запускаю этот unit test на ПК, поэтому бюджет памяти для виртуальной машины может быть довольно большим.

================== Добавлено позже ======================================================================= ===

Спасибо всем, кто позаботился ответить!

После рассмотрения всех pro и contra я решил пойти грубым способом, как советовали nanda и jarnbjo. Однако оказалось, что JVM не настолько тупой - он даже не попытается собрать сбор мусора, если вы попросите блок, который один больше, чем бюджет памяти VM. Поэтому я изменил код следующим образом:

    /* Force releasing SoftReferences */
    try {
        final List<long[]> memhog = new LinkedList<long[]>();
        while(true) {
            memhog.add(new long[102400]);
        }
    }
    catch(final OutOfMemoryError e) {
        /* At this point all SoftReferences have been released - GUARANTEED. */
    }

    /* continue the test here */

Ответ 1

Эта часть кода заставляет JVM очищать все SoftReferences. И это очень быстро сделать.

Он работает лучше, чем метод Integer.MAX_VALUE, так как здесь JVM действительно пытается выделить столько памяти.

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

Теперь я использую этот бит кода везде, где мне нужен код unit test, используя SoftReferences.

Обновить. Этот подход действительно работает только с макс. памятью менее 2 ГБ.

Кроме того, нужно быть очень осторожным с SoftReferences. Это так просто, чтобы сохранить жесткую ссылку по ошибке, которая будет отрицать эффект SoftReferences.

Вот простой тест, который показывает, что он работает каждый раз на OSX. Было бы интересно узнать, одинаково ли поведение JVM в Linux и 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!");
}

Ответ 2

Улучшение, которое будет работать более чем на 2 Гб памяти. Он перемещается до тех пор, пока не произойдет ошибка OutOfMemory.

@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));

}

Ответ 3

  • Задайте параметр -Xmx очень небольшое значение.
  • Подготовьте свой мягкий ссылка
  • Создайте столько объектов, сколько возможное. Запросите объект каждый раз, пока он снова не спросит объект с сервера.

Это мой маленький тест. Измените свою потребность.

@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);
  }
}

Ответ 4

Вы можете явно указать мягкую ссылку на нуль в своем тесте и, таким образом, имитировать, что мягкая ссылка была выпущена.

Это позволяет избежать любой сложной тестовой установки, которая зависит от памяти и сбора мусора.

Ответ 5

Вместо длинного цикла (как было предложено nanda), возможно, быстрее и проще просто создать огромный примитивный массив для выделения большего объема памяти, чем доступный для виртуальной машины, а затем поймать и игнорировать OutOfMemoryError:

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

Это очистит все слабые и мягкие ссылки, если у вашей виртуальной машины не будет доступной куча более 16 ГБ.