Может ли другой процесс (не java или нет), работающий в той же операционной системе и оборудовании, вызвать
java.lang.OutOfMemoryError: GC overhead limit exceeded
Потреблением оперативной памяти и / или значительной загрузкой процессора - или каким-либо другим способом?
Подробное сообщение «Превышен предел накладных расходов сборщика мусора» указывает на то, что сборщик мусора работает все время, а программа Java работает очень медленно. Если после сборки мусора процесс Java тратит более 98% своего времени на сборку мусора и восстанавливает менее 2% кучи ...
И этот несколько более старый поток Я понимаю, что это чувствительно ко времени. Однако, похоже, не хватает точных спецификаций того, к чему относятся эти 98%.
Изменить 20201008: добавлен Ссылка на эргономику сборщика мусора
1 ответ
Да, но в реальной жизни это маловероятно.
Чтобы JVM выбрасывала java.lang.OutOfMemoryError: GC overhead limit exceeded
, должны быть выполнены два условия:
- Цикл GC освобождает менее
GCHeapFreeLimit
(2%) места в куче; - JVM тратит на сборку мусора более
GCTimeLimit
(98%) времени.
Внешний процесс вряд ли может повлиять на первое условие, если он напрямую не взаимодействует с целевым приложением. Это означает, что JVM уже должна находиться в состоянии «почти не хватает памяти», чтобы произошла ошибка.
На что, вероятно, может повлиять другой процесс, так это на время. Если этот процесс интенсивно использует общие ресурсы ЦП, он может замедлить работу GC, конкурируя с JVM за процессорное время. Более медленный GC означает более длинные циклы GC и, следовательно, больший процент времени, проводимого в GC.
Мне удалось создать искусственный пример, когда другой процесс заставляет JVM бросать GC overhead limit exceeded
, но это было действительно сложно.
Рассмотрим следующую программу на Java.
import java.util.ArrayList;
public class GCOverheadLimit {
static ArrayList<Object> garbage = new ArrayList<>();
static byte[] reserve = new byte[100_000];
static void fillHeap() {
try {
while (true) {
garbage.add(new byte[10_000]);
}
} catch (OutOfMemoryError e) {
reserve = null;
}
}
public static void main(String[] args) throws Exception {
System.out.println("Filling heap");
fillHeap();
System.out.println("Starting GC loop");
while (true) {
garbage.add(new byte[10_000]);
garbage.remove(garbage.size() - 1);
Thread.sleep(20);
}
}
}
Во-первых, он заполняет всю кучу не подлежащими восстановлению объектами, оставляя небольшой резерв свободной памяти. Затем in повторно выделяет восстанавливаемый мусор, чтобы сборщик мусора повторялся снова и снова. Между итерациями есть небольшая задержка, чтобы общие накладные расходы GC не превышали 98%.
В эксперименте используется куча размером 1 ГБ и параллельный сборщик мусора:
java -Xmx1g -Xms1g -XX:+UseParallelGC GCOverheadLimit
Я запускаю эту программу в cgroup с квотой процессора. Моя машина имеет 4 ядра, но я позволяю JVM использовать процессорное время только 200 мс каждые 100 мс.
mkdir /sys/fs/cgroup/cpu/test
echo 200000 > /sys/fs/cgroup/cpu/test/cpu.cfs_quota_us
echo $JAVA_PID > /sys/fs/cgroup/cpu/test/cgroup.procs
Пока программа работает нормально. Теперь я запускаю один или два процесса записи процессора в одной контрольной группе:
sha1sum /dev/zero &
echo $! > /sys/fs/cgroup/cpu/test/cgroup.procs
Из-за превышения квоты ОС начинает блокировать процессы. Время сборки мусора увеличивается, и JVM, наконец, выбрасывает java.lang.OutOfMemoryError: GC overhead limit exceeded
.
Примечание: воспроизведение проблемы потребовало тщательного выбора параметров (размер кучи, задержки, квота). Параметры будут другими для других машин и других сред. Я хочу сказать, что проблема теоретически возможна, но, вероятно, никогда не возникнет на практике, поскольку существует слишком много факторов, которые необходимо согласовать друг с другом.
Похожие вопросы
Связанные вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.