← Назад к вопросам
Как понять причину зависания сервера, работающего на Java
3.0 Senior🔥 121 комментариев
#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Диагностика зависания Java сервера
Зависание (hang) — это состояние, когда приложение перестаёт отвечать на запросы. Это может быть вызвано deadlock, бесконечным циклом, memory leak или истощением ресурсов.
1. Проверка процесса
# Найти процесс Java
ps aux | grep java
ps -ef | grep java
# Получить PID
jps -l
jps -v # с параметрами
# Проверить CPU и память
top -p <PID>
htop -p <PID>
2. Thread Dump (Самый важный инструмент)
Thread dump показывает все потоки и их состояние.
# Получить thread dump (замени 1234 на PID)
jstack 1234 > thread_dump.txt
jstack 1234 | tee thread_dump.txt
# Несколько dumps с интервалом для сравнения
jstack 1234 > dump1.txt
sleep 5
jstack 1234 > dump2.txt
sleep 5
jstack 1234 > dump3.txt
# Отправить сигнал SIGQUIT (Linux/Mac)
kill -3 1234
# Dump будет в консоли/логах приложения
# Windows
jstack 1234
# или в JDK bin директории
%JAVA_HOME%/bin/jstack.exe 1234
Анализ Thread Dump
PIDLocked: это потоки, заблокированные друг на друге
javax.servlet.http.HttpServlet.service (HttpServlet.java:590)
at org.apache.catalina.connector.CoyoteAdapter.service (CoyoteAdapter.java:537)
at org.apache.coyote.http11.Http11Processor.process (Http11Processor.java:877)
...
- locked <0x00000000d6b6c8c0> (a java.lang.Object)
- waiting to lock <0x00000000d6b6c8e0> (a java.lang.Object)
Это указывает на deadlock!
3. Heap Dump (Для утечек памяти)
# Получить heap dump
jmap -dump:live,format=b,file=heap.bin <PID>
jmap -dump:format=b,file=heap.hprof <PID>
# Анализировать с помощью Eclipse MAT или JProfiler
# или через команду
jhat -J-Xmx1024m heap.hprof
# Автоматический heap dump при OutOfMemoryError
java -XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/logs/heap.bin \
-jar myapp.jar
4. GC Logs (Мониторинг сборки мусора)
# Запустить приложение с GC логированием
java -XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-XX:+PrintGCTimeStamps \
-Xloggc:/logs/gc.log \
-jar myapp.jar
# Проверить логи
tail -f /logs/gc.log
# Анализировать с помощью GCeasy.io или Splunk
5. Java Mission Control (JMC) и Flight Recorder
# Запустить приложение с flight recorder
java -XX:+UnlockCommercialFeatures \
-XX:+FlightRecorder \
-XX:FlightRecorderOptions=defaultrecording=true,dumponexit=true,dumponexitpath=/logs/recording.jfr \
-jar myapp.jar
# Подключиться к живому процессу
jcmd <PID> JFR.start name=myrecording
jcmd <PID> JFR.dump name=myrecording filename=/logs/recording.jfr
jcmd <PID> JFR.stop name=myrecording
# Открыть в JMC
jmc /logs/recording.jfr
6. Типичные проблемы и их диагностика
Deadlock (Взаимная блокировка)
// Проблемный код
public class DeadlockExample {
public static Object lock1 = new Object();
public static Object lock2 = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("T1: locked lock1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) { // T2 уже заблокировал lock2
System.out.println("T1: locked lock2");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("T2: locked lock2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) { // T1 уже заблокировал lock1
System.out.println("T2: locked lock1");
}
}
});
t1.start();
t2.start();
}
}
// В thread dump будет:
// "Found one Java-level deadlock:"
// T1: waiting to lock monitor 0x..., which is held by "T2"
// T2: waiting to lock monitor 0x..., which is held by "T1"
Бесконечный цикл / CPU выше 100%
# Найти горячий поток
top -p <PID> -H # показывает потоки
# Преобразовать tid в hex
printf "0x%x\n" <TID>
# В thread dump найти поток с этим nid
jstack 1234 | grep -A 20 "nid=0x..."
# Типичный вывод
nid=0x10a tid=0x12345 cpu=95.0%
at com.example.InfiniteLoop.run (InfiniteLoop.java:42)
at java.lang.Thread.run (Thread.java:834)
Memory Leak (Утечка памяти)
# Признаки
1. Медленно растущая heap (видно в GC logs)
2. FullGC не восстанавливает память
3. В итоге OutOfMemoryError
# Диагностика
jmap -histo <PID> | head -20
# Показывает топ объектов в памяти
# Если видишь много одинаковых объектов:
jmap -dump:format=b,file=heap.bin <PID>
# Открыть в Eclipse MAT
# Вкладка "Leak Suspects"
Thread Pool истощен
# В thread dump много потоков в WAITING:
Thread pool executor waiting for tasks
at java.util.concurrent.LinkedBlockingQueue.take
at java.util.concurrent.ThreadPoolExecutor.getTask
# Проверить очередь
jcmd <PID> Thread.print | grep "waiting to lock"
# Решение: увеличить pool size
java -XX:ThreadStackSize=512k \
-Djava.util.concurrent.ForkJoinPool.common.parallelism=8 \
-jar myapp.jar
7. Мониторинг в real-time
jstat (JVM Statistics Monitoring Tool)
# Мониторить GC
jstat -gc -h10 <PID> 1000 # каждую секунду
# Вывод:
# S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT
# 512k 512k 100k 0k 4096k 2000k 20480k 5000k 10240k 6000k 1024k 800k 100 2.5 5 0.8
# Мониторить потоки
jstat -threads <PID> 1000
# Мониторить классы
jstat -class <PID> 1000
JConsole
# Запустить
jconsole
# Подключиться к процессу
# Можно видеть:
# - Memory usage
# - Thread count
# - CPU usage
# - MBean attributes
8. Практический чеклист
# Шаг 1: Проверить, живой ли процесс
ps aux | grep java
# Шаг 2: Снять несколько thread dumps
jstack <PID> > dump1.txt
sleep 5
jstack <PID> > dump2.txt
# Шаг 3: Поискать deadlock
grep -i "deadlock" *.txt
grep "blocked" *.txt
# Шаг 4: Проверить CPU
top -p <PID> -H
# Шаг 5: Если CPU низкий, может быть блокировка
grep "waiting to lock" dump1.txt | wc -l
# Шаг 6: Если память растёт
jmap -histo:live <PID> | head -30
# Шаг 7: Если ничего не помогает, сделать heap dump
jmap -dump:live,format=b,file=heap.bin <PID>
# Анализировать в MAT
9. Проактивная защита
// Timeout для блокирующих операций
ExecutorService executor = Executors.newFixedThreadPool(10);
Future<String> future = executor.submit(() -> doSomething());
try {
String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.err.println("Task timed out!");
future.cancel(true);
}
// Deadlock detection
public void detectDeadlock() {
ThreadMXBean tmb = ManagementFactory.getThreadMXBean();
long[] deadlockedThreads = tmb.findDeadlockedThreads();
if (deadlockedThreads != null) {
System.err.println("DEADLOCK DETECTED!");
ThreadInfo[] infos = tmb.getThreadInfo(deadlockedThreads);
for (ThreadInfo info : infos) {
System.err.println(info);
}
}
}
Помни: быстрая диагностика требует постоянной подготовки. Снимай thread dumps регулярно и учись их читать!