Що таке stack trace?
Stack trace:
Junior Level
Визначення
Stack trace (стек викликів) — це список методів, які були викликані до виникнення винятку. Він показує шлях виконання коду від точки помилки до початку програми.
Stack frame (кадр стека) — це один елемент у ланцюжку викликів. Кожного разу, коли програма викликає метод, JVM створює «кадр» у пам’яті, який зберігає: локальні змінні методу, параметри, адресу повернення. Stack trace — це просто список таких кадрів, від поточного методу до main().
Приклад
public class Main {
public static void main(String[] args) {
method1();
}
static void method1() {
method2();
}
static void method2() {
throw new RuntimeException("Error!");
}
}
Stack trace:
java.lang.RuntimeException: Error!
at Main.method2(Main.java:12)
at Main.method1(Main.java:8)
at Main.main(Main.java:4)
Як читати
Читайте зверху вниз:
java.lang.RuntimeException: Error!
at Main.method2(Main.java:12)
at Main.method1(Main.java:8)
at Main.main(Main.java:4)
Розбір построчно:
java.lang.RuntimeException: Error!— тип винятку (RuntimeException) та повідомлення (Error!). Це найважливіший рядок: він каже ЩО сталосяat Main.method2(Main.java:12)— помилка сталася в методіmethod2класуMain, файлMain.java, рядок 12. Це той самий stack frame (кадр стека): метод, який був активний у момент помилкиat Main.method1(Main.java:8)—method2був викликаний зmethod1на рядку 8. Це попередній кадр стекаat Main.main(Main.java:4)—method1був викликаний зmainна рядку 4. Це корінь стека (точка входу)
Аналогія: уявіть слід із крихтів хліба. main() поклав крихту, викликав method1(), той поклав крихту і викликав method2(), де все зламалося. Stack trace — це шлях назад до початку.
Де побачити
- У консолі під час запуску програми
- У логах сервера
- При виклику
exception.printStackTrace()
Коли НЕ збирати stack trace (Performance Caveat)
Створення stack trace — дорога операція (~1-5 мікросекунд). JVM обходить усі кадри стека і копіює їх у масив.
Не збирайте stack trace, якщо:
- Винятки використовуються для потоку управління (антипатерн) — наприклад, кидати
NotFoundExceptionзамість поверненняOptional.empty(). При 1000 запитах на секунду це мілісекунди, які складаються - Високонавантажені системи — у hot-path (код, який викликається тисячі разів на секунду) створення винятку з повним стеком створює помітне навантаження на CPU та GC (garbage collector)
- Expected/normal flow — якщо
NumberFormatExceptionпри парсингу користувачського вводу — це очікувана ситуація, не логуйте повний стек, достатньо повідомлення
Альтернатива для легких винятків: у Java є трюк з fillInStackTrace() — можна перевизначити його у своєму винятку як return this;, щоб не збирати стек. Але тоді ви втратите інформацію про джерело помилки.
Middle Level
Внутрішній устрій
Stack trace — це масив об’єктів java.lang.StackTraceElement:
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
for (StackTraceElement el : elements) {
System.out.println(el.getClassName()); // "com.example.MyClass"
System.out.println(el.getMethodName()); // "myMethod"
System.out.println(el.getLineNumber()); // 42
System.out.println(el.getFileName()); // "MyClass.java"
System.out.println(el.isNativeMethod()); // false
}
Кожен елемент містить:
- Ім’я файлу
- Ім’я класу
- Ім’я методу
- Номер рядка (якщо компіляція з
-g) - Флаг нативного методу
StackWalker API (Java 9+)
До Java 9 getStackTrace() завжди копіював весь стек у масив — дорого.
StackWalker читає стек ліниво:
StackWalker.getInstance().forEach(frame ->
System.out.println(frame.getClassName() + " @ " + frame.getLineNumber())
);
Переваги:
- Ліниве читання (стримом)
- Пропуск непотрібних кадрів (проксі Spring, Hibernate)
- Доступ до об’єктів
Class, а не тільки рядкових імен
Реактивні стек-трейси
В асинхронному коді (Project Reactor / WebFlux) класичний стек-трейс марний — показує тільки нутрощі Netty threads.
Рішення: Hooks.onOperatorDebug() — «склеює» логічні шматки стека, але б’є по продуктивності.
Senior Level
FillInStackTrace Cost
Основний час — нативний обхід кадрів стека (fillInStackTrace). Чим глибше вкладеність методів (Spring!), тим дорожче створення винятку.
Stack Tracing у логах
Друк повного стека при кожному 404 — заб’є диск і задушить I/O.
Best Practice: повний стек тільки для ERROR, для очікуваних бізнес-винятків — тільки повідомлення та cause.
OmitStackTraceInFastThrow
При екстремально частих повтореннях одного винятку JVM (HotSpot) оптимізує його і перестає заповнювати стек-трейс:
java.lang.NullPointerException
// Без стека!
Флаг -XX:-OmitStackTraceInFastThrow вимикає цю оптимізацію для налагодження.
Obfuscation
При обфускації (Proguard/R8) стек-трейс перетворюється на a.b.c(SourceFile:1). Для розшифровки потрібні mapping-файли.
Діагностика
Thread.getAllStackTraces()— знімок усіх потоків. «Бідний» аналогjstackдля діагностики deadlock-ів.- Async Trace ID — у розподілених системах використовуйте
Span ID / Trace ID(OpenTelemetry) для зв’язку стеків між мікросервісами. jstack <pid>— повний дамп потоків з моніторами- JSON структуровані логи — стек-трейс як окреме індексоване поле в ELK
🎯 Шпаргалка для інтерв’ю
Обов’язково знати:
- Stack trace — список кадрів стека (stack frames) від точки помилки до
main() - Кожен кадр зберігає: ім’я класу, методу, номер рядка, ім’я файлу
- Створення stack trace — дорога операція (~1-5 мкс); JVM обходить усі кадри
StackWalker(Java 9+) читає стек ліниво, ефективніше заgetStackTrace()- Флаг
-XX:-OmitStackTraceInFastThrowвимикає оптимізацію JVM, яка прибирає стек при частих повтореннях - В асинхронному коді (WebFlux) класичний стек-трейс марний — показує тільки internals фреймворку
Часті уточнюючі запитання:
- Як читати stack trace? — Зверху вниз: перший рядок — тип та повідомлення винятку, далі — шлях від місця помилки до точки входу
- Коли НЕ збирати stack trace? — У hot-path (тисячі викликів/сек), при expected flow (валідація вводу), коли винятки використовуються для управління потоком
- Що таке
fillInStackTrace()? — Нативний метод, який обходить кадри стека; можна перевизначити якreturn thisдля пропуску (але втрачається інформація) - Як працює StackWalker? — Ліниве читання стека стримом, можна пропускати кадри, доступ до
Class-об’єктів а не тільки рядків
Червоні прапорці (НЕ говорити):
- “Stack trace безкоштовний — друкую завжди” — Створення стека навантажує CPU та GC, особливо в highload
- “В асинхронному коді стек-трейс показує повну картину” — У реактивному коді показує тільки internals Netty/EventLoop
- “OmitStackTraceInFastThrow — це баг” — Це оптимізація HotSpot для частих повторюваних винятків
Пов’язані теми:
- [[17. Що робить метод printStackTrace()]]
- [[18. Як правильно логовати винятки]]
- [[20. Чому не варто ковтати винятки (порожній catch)]]
- [[13. Чи можна створювати кастомні винятки]]
- [[29. Що таке chaining винятків]]