← Назад к вопросам

Как понять причину зависания сервера, работающего на 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 регулярно и учись их читать!

Как понять причину зависания сервера, работающего на Java | PrepBro