Как сделать так, чтобы система Java выпустила Soft References? - PullRequest
17 голосов
/ 24 сентября 2010

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

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

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

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

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

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

    /* 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 */

Ответы [ 5 ]

13 голосов
/ 28 сентября 2010

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

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

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

Теперь я использую этот бит кода везде, где мне нужно для модульного тестирования кода с использованием 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!");
}
3 голосов
/ 24 мая 2011

Улучшение, которое будет работать для памяти объемом более 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));

}
1 голос
/ 24 сентября 2010
  1. Установите параметр -Xmx на очень небольшое значение
  2. Приготовь свой софт ссылка
  3. Создать столько объектов, сколько возможный. Каждый раз запрашивайте объект, пока он не запросит объект с сервера снова.

Это мой маленький тест. Изменить как вам нужно.

@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 голосов
/ 24 сентября 2010

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

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

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

0 голосов
/ 24 сентября 2010

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

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

...