Принудительное явное удаление объекта Java - PullRequest
14 голосов
/ 01 февраля 2010

Я работаю на сервере Java, который обрабатывает много очень плотного трафика. Сервер принимает пакеты от клиентов (часто много мегабайт) и пересылает их другим клиентам. Сервер никогда явно не хранит какие-либо входящие / исходящие пакеты. Тем не менее, сервер постоянно сталкивается с OutOfMemoryException исключениями.

Я добавил System.gc() в компонент передачи сообщений сервера, надеясь, что память будет освобождена. Кроме того, я установил размер кучи JVM в гигабайт. Я все еще получаю столько же исключений.

Итак, мой вопрос таков: как я могу убедиться, что мегабайтные сообщения не помещаются в очередь на неопределенный срок (несмотря на то, что они не нужны)? Есть ли способ для меня, чтобы вызвать "удалить" на этих объектах, чтобы гарантировать, что они не используют мое пространство кучи?

        try
        {
           while (true)
            {
               int r = generator.nextInt(100);//generate a random number between 0 and 100
                Object o =readFromServer.readObject();
                sum++;
                // if the random number is larger than the drop rate, send the object to client, else
                //it will be dropped
                if (r > dropRate)
                {
                    writeToClient.writeObject(o);
                    writeToClient.flush();
                    numOfSend++;
                    System.out.printf("No. %d send\n",sum);
                }//if

            }//while
        }//try

Ответы [ 14 ]

19 голосов
/ 01 февраля 2010

Потоки объектов содержат ссылки на каждый объект, написанный / прочитанный из них. Это связано с тем, что протокол сериализации допускает обратные ссылки на объекты, появившиеся ранее в потоке. Возможно, вы сможете использовать этот дизайн, но используйте writeUnshared / readUnshared вместо writeObject / readObject. Я думаю, но не уверен, что это помешает потокам сохранять ссылку на объект.

Как говорит Коуэн, здесь также используется метод reset(). Самое безопасное, что можно сделать, это, вероятно, использовать writeUnshared, сразу за которым следует reset() при записи в ObjectOutputStream s

.
11 голосов
/ 01 февраля 2010

Когда JVM находится на краю OutOfMemoryError, он будет запускать GC.

Таким образом, вызов System.gc() заранее не решит проблему. Проблема должна быть решена где-то еще. Есть два основных способа:

  1. Пишите эффективный для памяти код и / или исправляйте утечки памяти в вашем коде.
  2. Дайте JVM больше памяти.

Использование Java Profiler может дать много информации об использовании памяти и возможных утечках памяти.

Обновление : согласно вашим изменениям с более подробной информацией о коде, вызывающем эту проблему, посмотрите Ответ Джеффа Риди в этой теме , который предлагает использовать ObjectInputStream#readUnshared() и ObjectOutputStream#writeUnshared() вместо. (Связанный) Javadocs также объясняет это довольно хорошо.

4 голосов
/ 01 февраля 2010

System.gc () является лишь рекомендацией для виртуальной машины Java. Вы называете это, и JVM может или не может запустить сборку мусора.

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

Первый случай может быть проанализирован профилировщиком, где вы пытаетесь выяснить, сколько ссылок еще живы. Хорошим показателем для использования памяти является рост потребления памяти вашим сервером. Если каждый дополнительный запрос немного увеличивает ваш Java-процесс, скорее всего, вы где-то храните ссылки (jconsole может быть хорошим началом)

Если вы принимаете больше данных, чем можете обработать, вам придется блокировать дополнительные запросы, пока другие не будут завершены.

3 голосов
/ 01 февраля 2010

Глядя на ваш код: ваши ObjectInput/OutputStream экземпляры создаются заново каждый раз, когда приходит или отправляется пакет, и если да, закрываются ли они правильно? Если нет, вы звоните reset() после каждого чтения / записи? Классы потока объектов сохраняют ссылку на все объекты, которые они видели (во избежание повторной отправки одного и того же объекта при каждом обращении к нему), предотвращая их сборку мусора. У меня была именно эта проблема около 10 лет назад - фактически я впервые использовал профилировщик для диагностики утечки памяти ...

3 голосов
/ 01 февраля 2010

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

Чтобы получить лучшее представление о том, что такое лучшие практики, прочитайте Эффективная Java, глава 2 - речь идет о «создании и уничтожении объектов»

2 голосов
/ 01 февраля 2010

Вы можете настроить сборку мусора в Java, но не можете принудительно.

2 голосов
/ 01 февраля 2010

Вы не можете явно принудительно удалить, но МОЖЕТЕ гарантировать, что ссылки на сообщения не хранятся, сохраняя только одну прямую ссылку в памяти, а затем используя объекты Reference для хранения собираемых мусором ссылок на нее.

Как насчет использования очереди (небольшого ограниченного размера) для обработки сообщений, а затем вторичной очереди SoftReference, которая подается в первую очередь? Таким образом, вы гарантируете, что обработка будет продолжена, НО также вы не получите ошибок памяти, если сообщения слишком велики (в этом случае справочная очередь будет сброшена).

1 голос
/ 01 февраля 2010

Сервер принимает пакеты от клиенты (часто много мегабайт) и пересылает их другим клиентам.

Ваш код, вероятно, полностью получает "пакеты" перед их пересылкой. Это означает, что ему требуется достаточно памяти для хранения всех пакетов до тех пор, пока они не будут полностью переадресованы, а когда эти пакеты имеют размер «много мегабайт», это означает, что вам действительно нужно много памяти. это также приводит к ненужной задержке.

Вполне возможно, что у вас также есть утечка памяти, но если вышеприведенное верно, этот дизайн с сохранением и пересылкой является вашей самой большой проблемой. Вероятно, вы можете сократить использование памяти на 95%, если вы перепроектируете приложение так, чтобы оно не получало пакеты полностью, а вместо этого передавало их напрямую клиентам, то есть считывало за один раз только небольшую часть пакета и немедленно передавало это клиентам. Нетрудно сделать это так, чтобы клиенты выглядели точно так же, как и при хранении и пересылке.

1 голос
/ 01 февраля 2010

Вам нужно выяснить, держите ли вы предметы дольше, чем необходимо. Первым шагом было бы получить профилировщик кейса, посмотреть на кучу и понять, почему объекты не собираются.

Несмотря на то, что вы дали JVM 1 ГБ, может случиться так, что ваше молодое поколение слишком маленькое, если множество объектов создаются очень быстро, что вынуждает их переходить в старшие поколения, где они не будут удалены так быстро.

Полезная информация о настройке ГХ: http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html

1 голос
/ 01 февраля 2010

Если вы получаете исключения OutOfMemory, что-то явно содержит ссылку на эти объекты. Вы можете использовать инструмент, такой как jhat , чтобы выяснить, где эти ссылки встречаются.

...