OutOfMemoryErrors даже после использования WeakReference для ключей и значений - PullRequest
5 голосов
/ 14 января 2010

Ниже приведен небольшой тест, который я написал для обучения API ссылок. Я думал, что это никогда не бросит OOME, но это бросает это. Я не могу понять, почему. признателен за любую помощь в этом.

public static void main(String[] args)
{
    Map<WeakReference<Long>, WeakReference<Double>> weak = new HashMap<WeakReference<Long>, WeakReference<Double>>(500000, 1.0f);

    ReferenceQueue<Long> keyRefQ = new ReferenceQueue<Long>();
    ReferenceQueue<Double> valueRefQ = new ReferenceQueue<Double>();

    int totalClearedKeys = 0;
    int totalClearedValues = 0;

    for (long putCount = 0; putCount <= Long.MAX_VALUE; putCount += 100000)
    {
        weak(weak, keyRefQ, valueRefQ, 100000);

        totalClearedKeys += poll(keyRefQ);
        totalClearedValues += poll(valueRefQ);

        System.out.println("Total PUTs so far    = " + putCount);
        System.out.println("Total KEYs CLEARED so far    = " + totalClearedKeys);
        System.out.println("Total VALUESs CLEARED so far = " + totalClearedValues);
    }

}

public static void weak(Map<WeakReference<Long>, WeakReference<Double>> m, ReferenceQueue<Long> keyRefQ,
        ReferenceQueue<Double> valueRefQ, long limit)
{
    for (long i = 1; i <= limit; i++)
    {
        m.put(new WeakReference<Long>(new Long(i), keyRefQ), new WeakReference<Double>(new Double(i), valueRefQ));
        long heapFreeSize = Runtime.getRuntime().freeMemory();
        if (i % 100000 == 0)
        {
            System.out.println(i);
            System.out.println(heapFreeSize / 131072 + "MB");
            System.out.println();
        }

    }
}

private static int poll(ReferenceQueue<?> keyRefQ)
{
    Reference<?> poll = keyRefQ.poll();
    int i = 0;
    while (poll != null)
    {
        // 
        poll.clear();
        poll = keyRefQ.poll();
        i++;
    }
    return i;

}

}

А ниже - журнал при работе с 64 МБ кучи

Total PUTs so far    = 0
Total KEYs CLEARED so far    = 77982
Total VALUESs CLEARED so far = 77980
100000
24MB

Total PUTs so far    = 100000
Total KEYs CLEARED so far    = 134616
Total VALUESs CLEARED so far = 134614
100000
53MB

Total PUTs so far    = 200000
Total KEYs CLEARED so far    = 221489
Total VALUESs CLEARED so far = 221488
100000
157MB

Total PUTs so far    = 300000
Total KEYs CLEARED so far    = 366966
Total VALUESs CLEARED so far = 366966
100000
77MB

Total PUTs so far    = 400000
Total KEYs CLEARED so far    = 366968
Total VALUESs CLEARED so far = 366967
100000
129MB

Total PUTs so far    = 500000
Total KEYs CLEARED so far    = 533883
Total VALUESs CLEARED so far = 533881
100000
50MB

Total PUTs so far    = 600000
Total KEYs CLEARED so far    = 533886
Total VALUESs CLEARED so far = 533883
100000
6MB

Total PUTs so far    = 700000
Total KEYs CLEARED so far    = 775763
Total VALUESs CLEARED so far = 775762
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at Referencestest.weak(Referencestest.java:38)
    at Referencestest.main(Referencestest.java:21)

Ответы [ 6 ]

4 голосов
/ 14 января 2010

от http://weblogs.java.net/blog/2006/05/04/understanding-weak-references

Я думаю, что использование HashMap, вероятно, является проблемой. Возможно, вы захотите использовать WeakHashMap

Чтобы решить "серийный номер виджета" проблема выше, самая простая вещь, которую нужно сделать это использовать встроенный класс WeakHashMap. WeakHashMap работает так же, как HashMap, кроме того, что ключи (не значения!) относятся к использованию слабых Рекомендации. Если ключ WeakHashMap становится мусором, его запись удаляется автоматически. Это позволяет избежать Подводные камни, которые я описал и не требует изменения, кроме переключения с HashMap для WeakHashMap. Если вы следуя стандартному соглашению ссылаясь на свои карты через карту интерфейс, никакой другой код не нужно даже быть в курсе изменений.

3 голосов
/ 14 января 2010

Суть проблемы, вероятно, в том, что вы заполняете свою кучу объектами WeakReference, слабые ссылки очищаются, когда у вас мало памяти, но сами ссылочные объекты - нет, поэтому ваша хэш-карта заполняется с load-load, если объекты WeakReference (не говоря уже о массиве объектов, который использует hashmap, который будет расти бесконечно), все указывают на ноль.

Решение, как уже указывалось, - это слабая хэш-карта, которая удаляет эти объекты, если они больше не используются (это делается во время размещения).

EDIT:
Как указал Кевин, у вас уже есть отработанная логика очереди ссылок (я не уделял достаточно пристального внимания), решение с использованием вашего кода состоит в том, чтобы просто убрать его из карты в точке, где был собран ключ. Именно так работает слабая хеш-карта (где опрос просто вызывается при вставке).

1 голос
/ 14 января 2010

Даже если ваши слабые ссылки отпустят то, на что они ссылаются, они все равно сами не будут переработаны.

Так что в конце концов ваш хеш заполняется ссылками на ничто и вылетает.

То, что вам нужно (если вы хотите сделать это таким образом), это иметь событие, вызванное удалением объекта, которое вошло и удалило ссылку из хеша. (что может вызвать проблемы с многопоточностью, о которых вам также необходимо знать)

0 голосов
/ 14 января 2010

Другие правильно указали, в чем проблема; например @roe, @ Билл К.

Но еще один способ решить эту проблему (кроме того, чтобы почесать голову, спросить SO и т. Д.) - это посмотреть, как работает рекомендуемый Солнцем подход. В этом случае вы можете найти его в исходном коде для класса WeakHashMap.

Есть несколько способов найти исходный код Java:

  • Если у вас есть достойная Java IDE для запуска, она должна показать вам исходный код любого класса в библиотеке классов.

  • Большинство загрузок J2SE J2SE включают исходные JAR-файлы (как минимум) для открытых классов API.

  • Вы можете специально загрузить полные исходные коды для релизов Java на основе OpenJDK.

Но подход ZERO EFFORT состоит в том, чтобы выполнить поиск в Google, используя полное имя класса с прикрепленным к нему «.java.html». Например, поиск «java.util.WeakHashMap.java.html» дает эту ссылку на первой странице результатов поиска.

И источник скажет вам, что стандартная реализация WeakHashMap явно опрашивает свою очередь ссылок, чтобы удалить устаревшие (то есть сломанные) слабые ссылки из набора ключей карты. Фактически, он делает это каждый раз, когда вы открываете или обновляете карту, или даже просто запрашиваете ее размер.

0 голосов
/ 14 января 2010

Другая проблема может заключаться в том, что Java по какой-то причине не всегда активирует свой сборщик garbadge при исчерпании памяти, поэтому вам может потребоваться вставить явные вызовы для активации сборщика. Попробуйте что-то вроде

if ((putCount% 1000) === 0) Runtime.getRuntime () дс ();.

в вашем цикле.

Редактировать: Похоже, что новые реализации Java от Sun теперь вызывают сборщик garbadge перед выбрасыванием OutOfMemmoryException, но я почти уверен, что следующая программа выдаст исключение OutOfMemmoryException с jre1.3 или 1.4

открытый класс Test { public static void main (String args []) { while (true) { байт [] данные = новый байт [1000000]; } } }

0 голосов
/ 14 января 2010

Я вообще не эксперт по java, но я знаю, что в .NET при выполнении большого выделения памяти для больших объектов вы можете получить фрагментацию кучи до такой степени, что для выделения будут доступны только небольшие куски непрерывной памяти, даже если больше памяти появляется как «свободная».

Быстрый поиск в Google по запросу " фрагментация кучи Java " приводит к некоторым, казалось бы, уместным результатам, хотя я не очень хорошо их рассмотрел.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...