Принудительное удаление объекта Java

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

Ответ 1

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

Ответ 2

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

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

Вы можете настроить сбор мусора в java, но вы не можете заставить.

Ответ 8

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

Ответ 9

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

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

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

Ответ 10

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

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

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

Ответ 11

Вручную запускать System.gc не является хорошим ответом, как другие опубликовали здесь. Он не гарантированно запускается, и он запускает полный gc, который, скорее всего, повесит ваш сервер в течение длительного времени, пока он работает ( > 1 с, если вы даете серверу GB RAM, я видел несколько минут длинные паузы в больших системах). Вы можете настроить свой gc, который, безусловно, поможет, но не полностью устранит проблему.

Если вы читаете объекты из одного потока, а затем записываете их в другой, тогда есть точка, в которой вы держите весь объект в памяти. Если эти объекты, как вы заявляете, велики, то это может быть вашей проблемой. Попытайтесь переписать свой IO так, чтобы вы читали байты из 1 потока и записывали их в другой, даже если явным образом не содержал полный объект (хотя я не вижу, как это будет работать с сериализацией/десериализацией объектов, если вам нужно проверить/проверить объекты).

Ответ 12

просто добавить все те предыдущие ответы: System.gc() не команду на JVM инициировать мусор collection..it является кротким направлением и ничего случиться не гарантирует. Спецификация JVM не позволяет поставщикам заходить на то, что нужно делать при вызовах gc. Поставщики могут даже вообще ничего не делать!

Ответ 13

Вы упомянули, что вам явно нужен весь полученный пакет, прежде чем вы сможете его отправить? Ну, это не значит, что вам нужно хранить все это в памяти, не так ли? Является ли это возможным архитектурным изменением для сохранения полученных пакетов во внешнее хранилище (возможно, ram-disk или DB, если даже SSD слишком медленный), а затем транслировать их непосредственно получателю, не загружая их полностью в память?

Ответ 14

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