Утечка памяти в объектах ConcurrentLinkedQueue $ Node

1

У меня есть система, в которой много потоков создает журналы, которые должны быть вставлены в бэкенд NoSql. Чтобы уменьшить сетевой трафик, я ввел буфер между сервером и сервером.

окружающая среда:

Java, JSP, Spring MVC, JDK 1.7 Apache-tomcat-6

Используемый буфер - ConcurrentLinkedQueue в java. Также реализован DBPushThread для получения журналов из очереди каждые 5 секунд и их вставки для поддержки. Мы использовали предложение() для вставки и опроса() для popping. Согласно javadoc poll() - https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html#poll%28%29, он будет извлекать элемент и обновлять глава очереди. Таким образом, этот узел никогда не ссылается и в конечном итоге собирает мусор.

Я запустил сервер на 1 день и заметил, что с течением времени сервер слишком вялый. Взял кучу дампа (hprof) сервера, используя JVisualVM, и при анализе заметил, что существует более 15 000,00 экземпляров объектов ConcurrentLinkedQueue $ Node. При проверке представления экземпляра я могу увидеть значение узла LinkedList (свойство "item"), а его ссылка на следующий узел (свойство "next") для большинства объектов установлена равной нулю. Означает, что эти объекты Node являются кандидатами на сбор мусора, но этого не происходит и разыгрываются объекты Node, сложенные в памяти. Изображение 174551

Фрагмент кода дополнения

public void add(Log log) {
        buffer.offer(log);
    }

Извлечение содержимого из очереди (здесь максимальный индекс всегда указывается как размер очереди)

public List<Log> getContents(int maxIndex) {
    List<Log> logs = new LinkedList<Log>();

    for (int i = 0; i < maxIndex; i++) {
        Log log = buffer.poll();
        logs.add(Log);
    }
    return logs;
}

Я сделал только буфер (который является одиночной очередью) в качестве переменной экземпляра. Все остальные являются локальными возможностями для функции.

Является ли ошибка с JDK 1.7, что заброшенные узлы никогда не получают сбор мусора?

ИЛИ

Нужно ли мне реализовать объединение объектов в ConcurrentLinkedQueue? Если да, то как я могу это достичь?

ИЛИ

Это ошибка с моим кодом?

Пожалуйста, направляйте.

  • 0
    Вы должны уточнить, что означает «лакх» или использовать фактическое число для пропущенных случаев. Я должен был гуглить, чтобы найти это определение и понять, что оно означает 100 000 (сто тысяч). Пожалуйста, учтите культурные различия между SO читателями.
  • 0
    ХОРОШО. Я исправил это.
Теги:
memory-leaks
jvm
java.util.concurrent

2 ответа

2
Лучший ответ

Поскольку8472 указал, проанализировал дамп и заметил, что это не проблема с методами ConcurrentLinkedQueue poll() и offer().

В нашей архитектуре concurrentLinkedQueue действует как буфер, в котором складываются журналы, и DBPushThread будет извлекать журналы из очереди CL и вставлять их в резервное хранилище. Бэкэнд используется для поиска эластичности.

Из-за проблем с периодической стабильностью/масштабированием при упругом поиске, вставка DBPushThread журналов в elasticsearch терпит неудачу и исключает исключение. Мы выбрасывали это исключение. Поскольку это поток, это будет исключение UnCaughtException, и родительский поток никогда не получит уведомление.

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

Мы контролировали систему примерно на один месяц, а объем памяти соответствовал. Спасибо the8472 за то, что он направил меня в правильном направлении

3

При проверке представления экземпляра я могу увидеть значение узла LinkedList (свойство "item"), а его ссылка на следующий узел (свойство "next") для большинства объектов установлена равной нулю.

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

На скриншоте он фактически выглядит как головка CLQ, так и хвостовая точка для экземпляра # 5, что заставляет задуматься о том, на что ссылаются все другие экземпляры узла.

Как правило, вам нужно проанализировать пути к корням GC, чтобы найти, что удерживает объекты.

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

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

  • 0
    Спасибо the8472 за ваш вклад. Я обновил решение ниже.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню