Гайд [Java] Учимся читать стактрейсы, краш-репорты и крашлоги JVM

А вы умеете читать краш-репорты?

  • Пока только стактрейсы и краш-репорты

    Голосов: 0 0.0%

  • Всего проголосовало
    4
Начинающий
Начинающий
Статус
Онлайн
Регистрация
5 Июн 2025
Сообщения
501
Реакции
24
Данный гайд несет в себе цель понять каким образом читать стак-трейсы ошибок в Java, краш-репорты Minecraft и немного информации о том, куда можно ориентироваться при чтении JVM краш-логов (спойлер - без дебага это будет не просто).

Умение читать ошибки, и понимать что они означают - это совсем не сложно. В данном гайде мы рассмотрим различные стак-трейсы, попробуем их разбить на логическую составляющую и будем учится находить места и причины подобных ошибок. Реальная "теория" в этом гайде будет преимущественно опускаться, дабы сделать упор на более простое восприятие материала.

Раздел 1. Стак-трейсы

По простому stacktrace'ы можно описать как список вызванных функций начиная от стартовой точки выполнения потока. Вместе с этим, stacktrace'ы обычно содержат в себе названия ошибок которые были "выкинуты" в процессе выполнения приложения, я думаю в 99% случаев stacktrace'ы вы будете видеть из-за случившихся ошибок.

Выглядят stacktrace'ы таким образом:
Java stacktrace:
Expand Collapse Copy
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null
    at com.example.MyClass.myMethod(MyClass.java:10)
    at com.example.MyClass.main(MyClass.java:5)

Давайте разберем его структурно на части.
  1. Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null- Первую строку можно так-же разбить на несколько элементов:
    • Exception in thread "main" - Говорит нам об том, что ошибка произошла в потоке с названием main
    • java.lang.NullPointerException - Тип(класс) ошибки которая случилась. В данном случае это NullPointerException (NPE), означающий что где-то у нас вместо существующего объекта был использован null, чего не должно было случится.
    • Cannot invoke "String.length()" because "str" is null - Описание ошибки, дословный перевод которого Невозможно выполнить "String.length()" поскольку "str" является null. Подобное описание должно послужить одним из главнейших ориентиров, т.к. оно буквально описывает причину по которой возникла ошибка в коде. А именно выполнился следующий код:
      Java code:
      Expand Collapse Copy
      String str = null; // Переменная имеет значение null
      /* ... */
      str.length(); // Пытаемся вызвать функцию length() у переменной со значением null
  2. at com.example.MyClass.myMethod(MyClass.java:10). После ошибки, у нас идут функции которые вызывались поочередно. От самой последней расположенной в начале, до самой первой расположенной в самом конце. В данном случае, пример кода приведённый выше (с str.length()) должен находится в функции указанной в этой строке, в пакете com.example, классе MyClass, в функции myMethod(...). Дополнительно у на есть сведения об исходном файле, и строке этого файла в которой находится код вызвавший ошибку (Файл MyClass.java, строка 10).
  3. at com.example.MyClass.main(MyClass.java:5). Последняя строка нашего stacktrace'а, и вторая строка с вызовом функции. Если функция не самая первая после ошибки в stacktrace'е, это значит что сама ошибка случилась не на ней, но указанная функция является звеном в списке вызовов при котором случилась ошибка. Если проще - Функция main(...) вызвала функцию myMethod(...), которая в свою очередь уже выкинула ошибку.

Так-же stacktrace'ы могут содержать несколько ошибок подряд. Выглядит это примерно так:
Java stacktrace:
Expand Collapse Copy
java.lang.RuntimeException: Failed due to exception.
    at org.example.MyClass.myMethod(Test.java:10)
    at org.example.MyClass.main(Test.java:5)
Caused by: java.lang.NullPointerException: myField1 is null
    at org.example.MyClass.myMethodWithNull(Test.java:16)
    at org.example.MyClass.myMethod(Test.java:10)
    ... 2 more

В данном случае первой ошибкой которая случилась является NullPointerException, после которой случилась ошибка RuntimeException. Как так? Всё просто!
Если мы посмотрим на код который выдал такой stacktrace, мы увидим что NullPointerException попал в try-catch блок, который выбрасывает RuntimeException если случается ошибка (На эту мысль так-же может навести "Caused by:" на 4-й строке stacktrace'а).
Java code (Test#myMethod(...)):
Expand Collapse Copy
try {
    myMethodWithNull(); // Выбрасывает NullPointerException
} catch (Exception exception) {
    throw new RuntimeException("Failed due to exception.", exception);
}

Для закрепления информации, предлагаю разобрать несколько реальных ошибок не связанных с Minecraft:
  • Пример ошибки при попытке получить значение массива с индексом, который превышает размер массива. (Если размер массива - 5 элементов, то последний его индекс будет 4, т.к. индексы массивов начинаются с нуля).
    Java stacktrace:
    Expand Collapse Copy
    Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
        at com.example.Main.main(Main.java:6)
  • Пример ошибки при попытке создать ArrayList с некорректным аргументом(значением начального размера "-1"). В основном подобное регулируется документацией, где объясняется какие данные можно передавать в функцию как параметр, а какие лучше не стоит.
    Java stacktrace:
    Expand Collapse Copy
    Exception in thread "main" java.lang.IllegalArgumentException: Negative capacity: -1
        at java.base/java.util.ArrayList.<init>(ArrayList.java:153)
        at com.example.Main.main(Main.java:3)
  • Пример ошибки валидации байткода (особенно популярно при написании своих обфускаторов, которые "захламляют" байткод некорректными опкодами, в следствии чего JVM не способна прочитать и убедится в корректности подгружаемого class-файла.
    Java stacktrace:
    Expand Collapse Copy
    Exception in thread "main" java.lang.VerifyError: Expecting a stack map frame at branch target 10
        at com.example.Main.main(Main.java:1)
  • Пример ошибки при загрузке нативных библиотек. Как правило, самая частая причина такой ошибки - отсутствие библиотеки в Java library path'е (аргумент -Djava.library.path=<library_path>. Сам library path фактически является списком директорий и файлов, где JVM будет производить поиск нативных библиотек для их загрузки (своеобразный аналог Java classpath).
    Java stacktrace:
    Expand Collapse Copy
    Exception in thread "main" java.lang.UnsatisfiedLinkError: no nativeLib in java.library.path
        at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2447)
        at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:809)
    at com.example.NativeLoader.<clinit>(NativeWrapper.java:5) // <clinit>!
    (Рекомендую обратить внимание на <clinit>, это означает что ошибка произошла при инициализации класса, или в "статическом конструкторе", при инициализации полей и т.п. Так-же существует и <init>, который обозначает конструктор класса.
  • Пример ошибки переполнения стека, как правило возникает когда случается бесконечная рекурсия, либо рекурсия достаточно продолжительная, что-бы забить стек. (При втором случае, можно обойтись расширением стека через аргумент -Xss=4M. Стандартный размер стека порядка 0.5-1мб).
    Java stacktrace:
    Expand Collapse Copy
    Exception in thread "main" java.lang.StackOverflowError
        at com.example.Recursive.call(Recursive.java:3)
        at com.example.Recursive.call(Recursive.java:3)
        ...
Дополнительно, стоило бы упомянуть о том, что ошибка может находится и по середине stacktrace'а, рассмотрим пример со
Пожалуйста, авторизуйтесь для просмотра ссылки.
:
1756489750885.png

Почему за ошибку можно посчитать NullPointerException, а не XxxException который случился раньше? Давайте представим, что XxxException это ошибка подключения/соединения с базой данных (к примеру у нас отсутствует интернет соединение). Будет ли нормой то, что при отсутствии интернета наше приложение умирает? Я думаю что это не нормально. По этой причине проблема находится в коде указанном под ошибкой NullPointerException, где отсутствует обработка исключений либо проверка на null.


Не стоит бояться ошибок и stacktrace'ов, они не страшные и как правило далеко не большие (если, конечно, вы не работаете в энтерпрайзе :roflanPominki:)
Пожалуйста, авторизуйтесь для просмотра ссылки.

1756489180160.png


Раздел 2. Краш-репорты и Minecraft

Crash-report Minecraft'а де-факто является тем-же stacktrace'ом, дополнительно украшенным информацией об системе, вашем устройстве и веселым "комментарием" разработчиков игры. Crash-report'ы в Minecraft практически всегда "многоуровневые"(имеют несколько ошибок в stacktrace'е), поэтому важно анализировать его весь, т.к. фактическая ошибка может быть где угодно. Поскольку это по большей части будут те-же stacktrace'ы, этот раздел будет больше про анализ ошибок которые можно встретить на этом форуме в разделе Minecraft'а, и пояснения как самостоятельно приходить к их решению.

Для начала давайте ознакомимся со структурой crash-report'а:
Crash-report:
Expand Collapse Copy
---- Minecraft Crash Report ----
// This doesn't make any sense!

Time: 01.09.2025, 00:00
Description: Initializing game

---------------------------------------------------------------------------------------

-- Head --
Thread: Main
Stacktrace:
    at org.example.MyClass.main(MyClass.java:5)

-- Initialization --
Details:
Stacktrace:
    at org.example.MyClass.main(MyClass.java:5)

-- System Details --
Details:
    Minecraft Version: 1.16.5
    Minecraft Version ID: 1.16.5
    Operating System: Windows 10 (amd64) version 10.0
    Java Version: 22.0.1, Oracle Corporation
    Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode, sharing), Oracle Corporation
    Memory: 96420272 bytes (91 MB) / 199229440 bytes (190 MB) up to 3206545408 bytes (3058 MB)
    ...

В краш репорте есть вполне интересные блоки на которые можно обратить внимание, если наша проблема зависима от сборки JVM или комплектующих компьютера (к примеру не проявляется с видеокартами AMD Radeon, но проявляется на видеоадаптерах Intel Iris). Что-ж за они?
  • Для начала, тот самый "веселый комментарий" от разработчиков игры. В зависимости от места где произошла ошибка внутри игры, описание будет рознится. Я думаю оно выступает сейчас скорее как традиция, т.к. подобные надписи в игре присутствуют уже более 10 лет.
    Funny comment:
    Expand Collapse Copy
    // This doesn't make any sense!
  • Затем идут дата когда был записан crash-report и описание от самой игры на какой стадии случилась ошибка.
    Timestamp:
    Expand Collapse Copy
    Time: 01.09.2025, 00:00
    Description: Initializing game
  • И начинается самое интересное, идут 2 stacktrace'а. Первый - отвечающий за основную часть stacktrace'а и ошибки в определенном потоке (в данном случае - в основном потоке (Main), Второй - отвечающий за стартовую точку приложения.
    Stacktrace:
    Expand Collapse Copy
    -- Head --
    Thread: Main
    Stacktrace:
        at org.example.MyClass.main(MyClass.java:5)
    
    -- Initialization --
    Details:
    Stacktrace:
        at org.example.MyClass.main(MyClass.java:5)
  • А в конце идёт системная информация, версия игры, Java, Java VM, информация об ядрах процессора, видеокарте, и прочем.
    Код:
    Expand Collapse Copy
    -- System Details --
    Details:
        Minecraft Version: 1.16.5
        Minecraft Version ID: 1.16.5
        Operating System: Windows 10 (amd64) version 10.0
        Java Version: 22.0.1, Oracle Corporation
        Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode, sharing), Oracle Corporation
        Memory: 96420272 bytes (91 MB) / 199229440 bytes (190 MB) up to 3206545408 bytes (3058 MB)
        ...

Как правило, crash-report'ов более чем достаточно для решения ошибки, но иногда случаются ошибки которые могут поломать механизм записи crash-report'ов. В таком случае необходимо открывать логи клиента/сервера и разбираться уже с ними, т.к. часто информация с crash-report'а дублируется и в логи, а если и нет - то как минимум сама ошибка там присутствовать обязана (если ваша ошибка не связана с крашем всей JVM).

Далее рассмотрим примеры ошибок и их решения. Рассматривать мы их будем в порядке начиная с самых простых, заканчивая более сложными.
  • [Топик] Начнем с простого, отсутствие главного класса для запуска. Достаточно прочитать что нам написано в ошибке.
    Код:
    Expand Collapse Copy
    Error: Could not find or load main class net.minecraft.client.main.Main
    Caused by: java.lang.ClassNotFoundException: net.minecraft.client.main.Main
    Ошибки подобного рода происходят когда JVM не может найти класс, который помечен как главный в файле META-INF/MANIFEST.MF, либо при запуске с указанием главного класса в обход манифеста.
  • Так-же существуют ошибки которые могут быть похожими на пример выше, однако могут иметь описание "Bad main class", либо класс может существовать но JVM отказывается его читать. В таком случае проблема может быть в нескольких местах:
    • Jar файл(фактически zip-архив) повреждён
    • Class файл повреждён
    • Class файл не проходит проверку Security Manager из-за несовпадения хеш-сумм, описанных в файле META-INF/MANIFEST.MF для этого файла.
  • [Топик] Ещё один довольно простой пример, тут у нас просто переполняется "куча"(Java Heap), в следствии чего у нас возникает ошибка OutOfMemoryError. Решение простое - дать приложению больше памяти через -Xmx=<MEM> аргумент.
    Crash-report:
    Expand Collapse Copy
    ---- Minecraft Crash Report ----
    // You're mean.
    
    Time: 27.03.2025, 21:24
    Description: Initializing game
    
    java.lang.OutOfMemoryError: Java heap space
        at java.desktop/java.awt.image.DataBufferInt.<init>(DataBufferInt.java:75)
        at java.desktop/java.awt.image.Raster.createPackedRaster(Raster.java:539)
        ...
  • [Топик] Здесь - часто встречающаяся проблема при итерации по хеш-мапам, при итерации по хеш-мапам модифицировать её нельзя, вследствии чего у нас возникает ConcurrentModificationException. Решение включает в себя изменение стратегии итерации по мапе, либо использование concurrency-safe коллекции вроде ConcurrentHashMap.
    Пожалуйста, авторизуйтесь для просмотра ссылки.
    .
    Java stacktrace:
    Expand Collapse Copy
    java.util.ConcurrentModificationException: null
        at java.util.HashMap$HashIterator.nextNode(HashMap.java:1597) ~[?:?]
        at java.util.HashMap$KeyIterator.next(HashMap.java:1620) ~[?:?]
        at java.lang.Iterable.forEach(Iterable.java:74) ~[?:?]
  • [Топик] Простая проблема с NullPointerException, подобный пример мы рассматривали в разделе про stacktrace'ы. Что интересно, последний вызов происходит в функции самого Minecraft'а, из чего можно сделать предположение что здесь использовались Mixin'ы для инъекции кода в код майнкрафта. А так-же описание ошибки даёт понять, что скорее всего вызов AntiPush#isState() производился через какое-то поле которое должно было хранить объект класса AntiPush, но хранило оно null, в следствии чего собственно наша ошибка и была "выкинута" игрой.
    Crash-report:
    Expand Collapse Copy
    ---- Minecraft Crash Report ----
    // Don't do that.
    
    Time: 25.04.2025, 15:05
    Description: Ticking player
    
    java.lang.NullPointerException: Cannot invoke "alpha.night.modules.impl.misc.AntiPush.isState()" because "antiPush" is null
        at net.minecraft.entity.player.PlayerEntity.isPushedByWater(PlayerEntity.java:1990)
        at net.minecraft.entity.Entity.handleFluidAcceleration(Entity.java:3273)
        at net.minecraft.entity.Entity.func_233567_aH_(Entity.java:1156)
        ...
  • [Топик] Пример посложнее, crash-report без описания ошибки. Это уже поинтереснее, т.к. нам придётся заглядывать в код. Изначально мы видим что проблема у нас находится в классе MsdfFont$Builder, в функции build(...), и сама ошибка происходит на строке 157. Отправляемся туда! Там-же видим что на 157 строке у нас происходит выброс ошибки по причине того, что поле data равно null. Если выбрасывается ошибка в таком случае, не сложно понять что поле data не должно быть null. В нашем случае так-же достаточно посмотреть на описание ошибки в коде, которое даёт нам понять что коду "Не удалось прочитать файл с данными шрифта"(Failed to read font data file:...). А значит пора бы убедится, всё ли в порядке у нас с файлом, существует ли он, и точно ли он такой, каким должен быть (поскольку читы, имхо, обычно довольно скудны на количество runtime-исключений в коде).
    Crash-report:
    Expand Collapse Copy
    ---- Minecraft Crash Report ----
    // This doesn't make any sense!
    
    Time: 13.03.2025, 18:36
    Description: Initializing game
    
    ---------------------------------------------------------------------------------------
    
    -- Head --
    Thread: Main
    Stacktrace:
        at org.excellent.client.utils.render.font.MsdfFont$Builder.build(MsdfFont.java:157)
        at org.excellent.client.utils.render.font.Font.create(Font.java:96)
        ...
    Java code (MsdfFont$Builder#build(...)):
    Expand Collapse Copy
    public MsdfFont build() {
        FontData data = IOUtils.fromJsonToInstance(this.dataFile, FontData.class);
        Texture texture = IOUtils.toTexture(this.atlasFile);
    
        if (data == null) {
            throw new RuntimeException("Failed to read font data file: " + this.dataFile.toString() + "; Are you sure this is json file? Try to check the correctness of its syntax.");
        }
        ...
    }
  • [Топик] Далее по списку у нас идёт crash-report с некорректным explicit-cast'ингом объектов и Gson'ом. В crash-report'е находим смотрим на первую-же строку stacktrace'а, идём сразу в файл разбираться почему у нас произошёл ClassCastException. Открыв код, мы видим что код написан глупо, поскольку никаких проверок на валидность объекта для cast'инга объектов попросту нету. Из-за этого, когда JsonParser возвращает JsonNull - всё ломается, потому-что мы этот объект пытаемся положить в поле с типом JsonObject несмотря на то, что они не могут быть приведены к типам друг друга, из-за того что они оба наследуются только от JsonElement. Решение - сделать проверку перед cast'ом объектов, да и всего-то.
    Crash-report:
    Expand Collapse Copy
    ---- Minecraft Crash Report ----
    // You're mean.
    
    Time: 10.03.2025, 17:14
    Description: Initializing game
    
    java.lang.ClassCastException: class com.google.gson.JsonNull cannot be cast to class com.google.gson.JsonObject (com.google.gson.JsonNull and com.google.gson.JsonObject are in unnamed module of loader 'app')
        at eva.ware.config.ConfigStorage.loadConfiguration(ConfigStorage.java:72)
        at eva.ware.config.ConfigStorage.setupFolder(ConfigStorage.java:30)
    Java code (ConfigStorage#loadConfiguration(...):
    Expand Collapse Copy
    public void loadConfiguration(String configuration) {
        Config config = findConfig(configuration);
        try {
            FileReader reader = new FileReader(config.getFile());
            JsonParser parser = new JsonParser();
            JsonObject object = (JsonObject) parser.parse(reader); // Нету проверки перед cast'ом
            config.loadConfig(object);
        } catch (FileNotFoundException e) {
            ...
        } catch (NullPointerException pointerException) {
            ...
        } // Нету catch-блока для ClassCastException для улавливания такого рода проблем
    }
  • [Топик] Пришла пора задачек поинтереснее, где stacktrace вроде-бы подсказывает что не так, но решение может быть неочевидным. Такая задачка есть с чита Excellent Recode где используется конченая эвентная шина
    Пожалуйста, авторизуйтесь для просмотра ссылки.
    , которая использует LambdaFactory в качестве 1 из механизмов диспетчеризации эвентов. И теперь внимание, самая распространенная проблема связанная с этим, появляется при... рефакторинге названий пакетов чита. Звучит ужасно, потому-что судя по всему LambdaFactory под капотом используют текстовые названия пакетов и т.п. Суть заключается в том, что в главном классе чита(org.excellent.client.Excellent) есть функция start(), которая и является проблемой, т.к. при переименовании всего чита, IDE не знает о том, что где-то есть String который должен ориентироваться на название пакета, в следствии чего про это место много кто забывал, а после ничего и не работало из-за этого мягко-говоря странного механизма регистрации слушателей эвентов. Решение - нужно изменить значение String на актуальное название пакета.
    Crash-report:
    Expand Collapse Copy
    ---- Minecraft Crash Report ----
    // Don't do that.
    
    Time: 08.07.2025, 13:55
    Description: Initializing game
    
    ---------------------------------------------------------------------------------------
    
    -- Head --
    Thread: Main
    Stacktrace:
    at org.excellent.client.api.events.orbit.EventBus.getLambdaFactory(EventBus.java:257)
    at org.excellent.client.api.events.orbit.EventBus.getListeners(EventBus.java:234)
    at org.excellent.client.api.events.orbit.EventBus.getListeners(EventBus.java:223)
    at org.excellent.client.api.events.orbit.EventBus.lambda$getInstanceListeners$2(EventBus.java:218)
    at java.base/java.util.Map.computeIfAbsent(Map.java:1054)
    at java.base/java.util.Collections$SynchronizedMap.computeIfAbsent(Collections.java:2760)
    at org.excellent.client.api.events.orbit.EventBus.getInstanceListeners(EventBus.java:218)
    at org.excellent.client.api.events.orbit.EventBus.subscribe(EventBus.java:165)
    at org.excellent.client.api.events.Handler.<init>(Handler.java:8)
    Java code (Excellent#start()):
    Expand Collapse Copy
    public void start() {
        eventHandler.registerLambdaFactory(devMode ? "org.excellent" : "exc", (lookupInMethod, klass) -> (MethodHandles.Lookup) lookupInMethod.invoke(null, klass, MethodHandles.lookup()));
    }


Раздел 3. JVM краш-логи

Пришло время самой тяжелой темы, которую даже я до сих пор особо не понимаю, но с которой мне приходится работать чуть ли не каждый день. JVM краш-логи (они-же файлы формата hs_err_pid_XXXXX.log). Файлы которые прочитать сложнее, которые появляются когда умирает вся JVM и как-то обработать эту ошибку внутри Java попросту не получится. Поэтому я попробую дать хотя-бы немного вводных на что можно обращать внимание при поиске решений, и разберем пару ситуации произошедших на форуме и в моей практике, но как обычно, для начала - структура:
JVM Crash log:
Expand Collapse Copy
// Заголовок с "Сигнальной" ошибкой, проблемным фреймом и прочей информацией
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ffdcf73ca03, pid=30616, tid=32784
#
# JRE version: Java(TM) SE Runtime Environment (17.0.11+7) (build 17.0.11+7-LTS-207)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (17.0.11+7-LTS-207, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, windows-amd64)
# Problematic frame:
# C  [myNativeLibrary.dll+0x4ca03]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of Windows
#
# If you would like to submit a bug report, please visit:
#   https://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

// Общая информация
---------------  S U M M A R Y ------------

// Командная строка с которой было запущено приложение
Command Line: -XX:+ShowCodeDetailsInExceptionMessages...

// Информация об процессоре, сборке ОС, времени запуска
Host: AMD Ryzen 7 7700 8-Core Processor              , 16 cores, 63G,  Windows 10 , 64 bit Build 19041 (10.0.19041.5915)
Time: Thu Jul  3 18:44:40 2025 RTZ 2 (s 10 , 64 bit Build 19041 (10.0.19041.5915) elapsed time: 1.897355 seconds (0d 0h 0m 1s)

// Информация об потоке
---------------  T H R E A D  ---------------

Current thread (0x000001dcca6532c0):  JavaThread "main" [_thread_in_native, id=32784, stack(0x000000c221a00000,0x000000c221b00000)]

Stack: [0x000000c221a00000,0x000000c221b00000],  sp=0x000000c221afedd0,  free space=1019k

// Нативные фреймы вне JVM
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [myNativeLibrary.dll.dll+0x4ca03]

// Фреймы внутри JVM
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  org.lwjgl.system.JNI.invokePC(JJ)S+0
j  org.example.Main.doThatThing()V+16
v  ~StubRoutines::call_stub

// "Сигнальная" ошибка
siginfo: EXCEPTION_ACCESS_VIOLATION (0xc0000005), reading address 0xffffffffffffffff

Registers:
RAX=0x007e0041004e0045, RBX=0x000001dd3c6136d8, RCX=0x00007ffdcfb0b038...

// Регистры указывающие на память чего-то
Register to memory mapping:
RIP=0x00007ffdcf73ca03 myNativeLibrary.dll
RAX=0x007e0041004e0045 is an unknown value
RBX={method} {0x000001dd3c6136e0} 'invokePC' '(JJ)S' in 'org/lwjgl/system/JNI'
...

// Дамп памяти стека
Top of Stack: (sp=0x000000c221afedd0)
0x000000c221afedd0:   00007ffdcfbad000 000001dd490eb3f0
...


// Информация об процессе, JVM и событиях внутри JVM
---------------  P R O C E S S  ---------------

// Java потоки
Java Threads: ( => current thread )
=>0x000001dcca6532c0 JavaThread "main" [_thread_in_native, id=32784, stack(0x000000c221a00000,0x000000c221b00000)]
  0x000001dcf7565030 JavaThread "Reference Handler" daemon [_thread_blocked, id=88892, stack(0x000000c222100000,0x000000c222200000)]
  0x000001dcf7565bf0 JavaThread "Finalizer" daemon [_thread_blocked, id=89628, stack(0x000000c222200000,0x000000c222300000)]
  0x000001dcfa47b7f0 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=88788, stack(0x000000c222300000,0x000000c222400000)]
  0x000001dcfa47c3b0 JavaThread "Attach Listener" daemon [_thread_blocked, id=89976, stack(0x000000c222400000,0x000000c222500000)]
  ...

// JVM-Потоки (которыми владеет к примеру виртуальная машина)
Other Threads:
  0x000001dcf7561590 VMThread "VM Thread" [stack: 0x000000c222000000,0x000000c222100000] [id=91604]
  0x000001dcf73c1bc0 WatcherThread [stack: 0x000000c223200000,0x000000c223300000] [id=89868]
  0x000001dcf73b0470 GCTaskThread "GC Thread#0" [stack: 0x000000c221b00000,0x000000c221c00000] [id=34836]
  ...

// Информация об "Куче"(Java Heap)
Heap:
 garbage-first heap   total 1040384K, used 4964K [0x000000040c800000, 0x0000000800000000)
  region size 8192K, 2 young (16384K), 1 survivors (8192K)
 Metaspace       used 8093K, committed 8256K, reserved 1114112K
  class space    used 708K, committed 768K, reserved 1048576K

// Участки памяти "Кучи"(Java Heap)
Heap Regions: E=young(eden), S=young(survivor), O=old, HS=humongous(starts), HC=humongous(continues), CS=collection set, F=free, OA=open archive, CA=closed archive, TAMS=top-at-mark-start (previous, next)
...

// Данные использования памяти Metaspace(Java Metaspace)
Metaspace: ...

// Внутренняя статистика памяти
Internal statistics:
...

// Последние события JIT-компиляции
Compilation events (20 events): ...

// Прочие события касающиеся JIT и JVM
Deoptimization events (20 events): ...
Classes unloaded (0 events): ...
Classes redefined (0 events): ...
Internal exceptions (20 events): ...
VM Operations (14 events): ...
Events (20 events): ...

// Подключенные динамические библиотеки
Dynamic libraries:
0x00007ff61d300000 - 0x00007ff61d310000     C:\Program Files\Java\jdk-17\bin\javaw.exe
0x00007ffe16ef0000 - 0x00007ffe170e8000     C:\Windows\SYSTEM32\ntdll.dll
0x00007ffe156e0000 - 0x00007ffe157a2000     C:\Windows\System32\KERNEL32.DLL
0x00007ffe14720000 - 0x00007ffe14a16000     C:\Windows\System32\KERNELBASE.dll

// JVM аргументы использованные при запуске
VM Arguments:
jvm_args: -XX:+ShowCodeDetailsInExceptionMessages -agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:58991 -Dfile.encoding=UTF-8...

// Главный класс использованный для запуска
java_command: org.example.Main

// Java classpath
java_class_path (initial): ...

// Информация об системе
---------------  S Y S T E M  ---------------

OS:
 Windows 10 , 64 bit Build 19041 (10.0.19041.5915)
OS uptime: 3 days 15:01 hours

CPU: total 16 (initial active 16) (16 cores per cpu, 2 threads per core) family 25 model 97 stepping 2 microcode 0x0, cx8, cmov, fxsr, ht, mmx, 3dnowpref, sse, sse2, sse3, ssse3, sse4a, sse4.1, sse4.2, popcnt, lzcnt, tsc, tscinvbit, avx, avx2, aes, erms, clmul, bmi1, bmi2, adx, avx512f, avx512dq, avx512cd, avx512bw, avx512vl, sha, fma, vzeroupper, avx512_vpopcntdq, avx512_vpclmulqdq, avx512_vaes, avx512_vnni, clflush, clflushopt, avx512_vbmi2, avx512_vbmi

Memory: 4k page, system-wide physical 64730M (40013M free)
TotalPageFile size 103642M (AvailPageFile size 67833M)
current process WorkingSet (physical memory assigned to process): 266M, peak: 267M
current process commit charge ("private bytes"): 1429M, peak: 1430M

vm_info: Java HotSpot(TM) 64-Bit Server VM (17.0.11+7-LTS-207) for windows-amd64 JRE (17.0.11+7-LTS-207), built on Mar 11 2024 19:01:50 by "mach5one" with MS VC++ 17.6 (VS2022)

END.
Обратим внимание всего на 2 блока, и рассмотрим только их. Если-же вам интересно как-же читать более подробно подобные краш-логи, вот вам два видео с конференций, там более подробно про это рассказывают:
Пожалуйста, авторизуйтесь для просмотра ссылки.
,
Пожалуйста, авторизуйтесь для просмотра ссылки.
.

Нам интересно следующее:
JVM Crash log:
Expand Collapse Copy
// Нативные фреймы вне JVM
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [myNativeLibrary.dll.dll+0x4ca03]

// Фреймы внутри JVM
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  org.lwjgl.system.JNI.invokePC(JJ)S+0
j  org.example.Main.doThatThing()V+16
v  ~StubRoutines::call_stub
Думаю взглянув один раз, возникнет чувство что это чем-то похоже на обычный stacktrace джавы. Собственно по нему можно ориентироваться дабы искать проблемы, просто если проблема в нативной библиотеке, а вы запустились без запущенного дебаггера - найти проблему будет сильно сложнее. Ниже я опишу пока-что 1 из случаев когда получилось обойтись без дебаггеров и понять где и как происходит проблема прибегая лишь к оффициальной документации и исходникам библиотек.
  • [Топик] Рассмотрим недавний пример, где по какой-то причине из-за нативной библиотеки умирала вся JVM. Первым делом получили JVM краш-лог, и смотрим что же там такое. На наше счастье там таки была подсказка которая вполне себе даёт понять что проблема очевидно на стороне нативной библиотеки (А именно siginfo - EXCEPTION_UNCAUGHT_CXX_EXCEPTION, что судя по названию даёт понять, что у нас есть неперехваченное исключение(ошибка), и что это произошло на стороне нативной библиотеки(_CXX_, то-же самое что и C++). Так-же сам краш-лог содержал инфу о нативной функции в которой произошла ошибка. Фактически решением в данной ситуации было бы дописать и пересобрать библиотеку да-бы при ошибках она просто возвращала null на стороне JVM, и проблема бы ушла, пусть иногда и возвращая null как результат. (Почему SMTC(GlobalSystemMediaTransportControlsSessionManager#RequestAsync()) иногда проваливается при закрытии приложении для меня загадка, мельком глядя в документации ничего не заметил).
    JVM Crash log:
    Expand Collapse Copy
    Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
    C  [KERNELBASE.dll+0xc9f0a]
    C  [VCRUNTIME140.dll+0x6480]
    C  [MediaPlayerInfo.dll+0x3c0e]
    
    Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
    J 30236  dev.redstones.mediaplayerinfo.impl.win.WindowsMediaPlayerInfo.getMediaSessions()Ljava/util/List; (0 bytes) @ 0x000001d54e841893 [0x000001d54e841840+0x0000000000000053]
    J 32720 c1 ru.mytheria.api.util.media.MediaPlayer.lambda$onTick$1()V (384 bytes) @ 0x000001d54925cea4 [0x000001d54925cd40+0x0000000000000164]
    J 32719 c1 ru.mytheria.api.util.media.MediaPlayer$$Lambda+0x000001d56083e3b8.run()V (8 bytes) @ 0x000001d54925c654 [0x000001d54925c5c0+0x0000000000000094]
    ...
    
    siginfo: EXCEPTION_UNCAUGHT_CXX_EXCEPTION (0xe06d7363), ExceptionInformation=0x0000000019930520 0x0000000ca7bfeab0 0x00007fff4b289b40 0x00007fff4b280000
    C++ code:
    Expand Collapse Copy
    jobject Java_dev_redstones_mediaplayerinfo_impl_win_WindowsMediaPlayerInfo_getMediaSessions(JNIEnv* env, jobject obj) {
        ...
        // Вероятное место ошибки, фактически их несколько с Async-получением объекта.
        auto smtc = GlobalSystemMediaTransportControlsSessionManager::RequestAsync().get();
        auto sessions = smtc.GetSessions();
        ...
    }
    Если кому-то интересно, для чела я писал альтернативную реализацию на Windows с реализациями на
    Пожалуйста, авторизуйтесь для просмотра ссылки.
    и
    Пожалуйста, авторизуйтесь для просмотра ссылки.
    , можете ознакомится с разницей между
    Пожалуйста, авторизуйтесь для просмотра ссылки.
    и утечкой памяти по JVM референсам.

Заключение

Спасибо за прочтение.

На самом деле тема не то что-бы большая(если не брать в учёт JVM краш-логи), но примерами её можно раздувать бесконечно. Бояться исключений, ошибок, краш-репортов и краш-логов не нужно, это обычные вещи цель которых не дать случится ошибкам похлеще(вроде синего экрана, либо зависания намертво).

Если у вас есть какие-то интересные примеры stacktrace'ов, crash-report'ов или JVM краш-логов, не стесняйтесь ими делиться, самое интересное я добавлю в статью указав ваш тег!
 
Последнее редактирование:
Назад
Сверху Снизу