На прошлой неделе появилась онлайн-программа обратной связи по эксплуатации и техническому обслуживанию OOM, и вывод в журнале программы был следующим:
Exception in thread "http-nio-8080-exec-1027" java.lang.OutOfMemoryError: Java heap space
Exception in thread "http-nio-8080-exec-1031" java.lang.OutOfMemoryError: Java heap space
Имя потока должно быть рабочим потоком nio tomcat. Когда поток обрабатывает программу, возникает OOM, потому что он не может выделить больше памяти в куче. К счастью, параметр запуска JVM настроен с -XX: + HeapDumpOnOutOfMemoryError и использует MAT открыть полученный файл hprof для анализа.
Первый шаг — открыть гистограмму, чтобы увидеть, какие объекты занимают больше всего памяти:
max-http-header-size: 10000000
До сих пор в основном было установлено, что проблема вызвана этим необоснованным максимальным параметром заголовка HTTP-запроса. Тут еще 3 вопроса:
- Даже если запрос выделяет 10 МБ памяти, а куча имеет 8 ГБ, так ли много параллелизма в это время? 800 потоков кота?
- Параметр устанавливает только максимальный заголовок запроса 10 М. Почему tomcat выделяет такой большой буфер за один раз?
- Почему так много потоков tomcat? Я чувствую, что программа не так много параллельна.
Давайте сначала рассмотрим вопрос 1, который можно продолжить, чтобы найти ответ в дампе через МАТ. Вы можете открыть представление потоков и найти рабочие потоки tomcat и обнаружить, что количество потоков действительно равно 401, но это только половина от 800:
Давайте посмотрим на вопрос 2, который требует от вас найти исходный код. Прежде всего, max-http-header-size — это параметр, определенный Springboot. Глядя на код Springboot, вы можете видеть, что этот параметр — MaxHttpHeaderSize, установленный для tomcat :
<attribute name="socket.appReadBufSize" required="false">
<p>(int)Each connection that is opened up in Tomcat get associated with
a read ByteBuffer. This attribute controls the size of this buffer. By
default this read buffer is sized at <code>8192</code> bytes. For lower
concurrency, you can increase this to buffer more data. For an extreme
amount of keep alive connections, decrease this number or increase your
heap size.</p>
</attribute>
Вот почему раньше я видел большое количество буферов размером 10008192 байта. Очевидно, что есть еще пакет пустых буферов размером 10000000 байт, которые должны быть буферами вывода.Давайте посмотрим на исходный код:
Что касается вопроса 3, очевидно, что наше приложение настроено с самым большим потоком (проверив конфигурацию, мы обнаружили, что мы настроили 2000, что немного велико), иначе не будет 401 рабочего потока (по умолчанию 150), если concurrent в то время Если он не большой, это возможно. Запрос очень медленный. Хотя параллелизм невелик, требуется больше потоков, потому что выполнение запроса медленное. Например, если TPS составляет 100, но в среднем RT — 4 с, это 400 потоков. Ответ на этот вопрос все еще можно найти через MAT.Если вы посмотрите на несколько потоков, вы можете обнаружить, что многие потоки ждут возврата внешнего сервиса, а это означает, что внешний сервис работает относительно медленно. журнал программы в то время, вы можете обнаружить, что есть много «feign.RetryableException»: чтение журнала выполнения истекло по тайм-ауту». . . . Преследуй и убивай вниз по течению! Притормози, тайм-аут нашего симуляции тоже нужно выставить заново, так что не тяни насмерть внешние сервисы.