Конец трассировки внутреннего стека исключений что это

от admin

выход из команды "node node_modules/webpack/bin/webpack.js —env.prod" с кодом 2

Чтобы я не делал у меня не получается опубликовать приложение. Работаю в Visual Studio 2017, использовал встроенный шаблон Angular. В режиме отладки все работает просто отлично. Он стоит попытаться опубликовать постоянно выбрасывает следующую ошибку:

И отсылает в файл с подробностями:

Я пробовал выполнять команду «node node_modules / webpack / bin / webpack.js -env.prod» но ничего похожего как здесь http://www.programfaqs.com/faq/error-msb3073-the-command-node-node_modules-webpack-bin-webpack-js-env-prod-exited-with-code-2-2/ Но не добился указанных результатов.

Может я не там делал? Я выполнял эту команду в командной строке и администраторе Node.js в папке с файлом sln

И в следующей папке пытался. И в папке до папки с файлом sln. Не понимаю в чем дело

Удалось решить проблему. Оказалось моя вина, просто не доглядел. Надо все делать внимательно. Если вкратце, то надо просто внимательнее смотреть параметры и методы, который вы создаете в компоненте и используете в html коде. Сейчас я просто опишу процесс решения проблемы. Так как изначально я не мог даже посмотреть в чем дело, мне выдавалась следующая ошибка:

Я просто сделал все публичным, как советовалось в этой статье https://www.ryadel.com/en/the-command-node-node_modules-webpack-bin-webpack-js-env-prod-exited-with-code-2-error-fix-angular-node/. Не знаю повлияло ли это как-то на решение проблемы (у меня просто не было подробностей ошибки) но проблема не исчезла. Затем спустя какое-то время я просто случайно увидел, что не правильно выполняю изначальную команду. Я выполнял ее так:

А надо было так:

После этого появились следующие подробности:

Теперь было видно где ошибка. Ну а дальше было просто. У меня был примерно следующая проблемная часть кода:

Я просто закомментировал все места где ссылался на что-нибудь из файла .ts и потихоньку раскомментировал строчки пока не обнаружил виновницу. Ей оказалась вот эта строчка:

В файле .ts она ссылалась на следующий метод:

Я исправил этот момент так:

И это логично, однако не понятно почему при публикации он на такое жалуется, а в отладке игнорирует и все выполняет нормально.

Трассировка стека и более точная отладка

Не так давно младший разработчик прислал мне запутанную трассировку стека и был очень удивлен, когда я мгновенно понял проблему и указал на необходимые изменения. Честно говоря, у меня было преимущество: ведь это как раз я поместил туда баг. Но все же способность извлекать информацию из трассировки стека, даже запутанной, — серьезный и важный навык.

Трассировка стека, о которой шла речь выше — ClassNotFoundException. Обычно тут ничего сложного: она уже говорит вам все, что нужно знать. Класса нет. Почему его нет — вот ключ к ответу на вопрос, что мы сделали не так. В этом случае, поскольку проект был обфусцирован, ошибка заключалась в том, что этот класс не был исключен из обфускации.

Несмотря на всю ненависть, которую оно вызывает на протяжении многих лет, исключение NullPointerException — одно из моих любимых. Вы мгновенно понимаете, что произошло, и в большинстве случаев стек приводит непосредственно к источнику проблемы. Бывают и спорные случаи, например:

Итак, что же здесь вызвало исключение NullPointerException ?

Если вы пользуетесь Java последней версии, он подскажет вам, что довольно круто. Но если мы все еще работаем на Java 11 (или 8), есть несколько вариантов. По крайней мере три или даже четыре, если слегка прищуриться:

  • myList — очевидный кандидат, но он редко принимает значение null , и если так, вы сразу это заметите;
  • offset может равняться null . Допустим, это целочисленный объект Integer , и в этом случае он может принять значение null из-за авто-упаковки. Что также маловероятно.
  • Наиболее вероятный виновник в данном конкретном случае — возвращаемое значение метода get() , то есть один из элементов списка имеет значение null .
  • Наконец, сам метод invokeMethod может вызвать исключение NullPointerException . Но это немного обман, так как стек будет глубже.

Таким образом, ничего не зная о коде, мы в существенной степени можем догадаться, что произошло, просто зная тип исключения. Но не всегда это приводит нас непосредственно к ошибке.

Здесь был null

Рассмотренное исключение NullPointerException , вероятно, произошло из-за значения null в списке — если допустить, что вы удостоверились, что по-прежнему не знаете, как он попал в список…

Это нетрудно выяснить. Предположим, список имеет тип ArrayList . В этом случае просто откройте класс ArrayList (допустим, с помощью сочетания клавиш Control-O в IntelliJ) и поместите условную точку останова в метод add() . Так вы протестируете, равно ли значение null , и что прекратит выполнение в точке останова, если кто-то попытается добавить null в список.

Но так не получится поймать все случаи попадания null в список. Это можно сделать с помощью Stream API , addAll() и нескольких других методов. Приятно, что мы можем воспользоваться практически любым из них:

Поскольку addAll() принимает коллекцию, мы можем использовать стандартный метод contains() , чтобы проверить, есть ли у нас нулевой элемент в коллекции, и если да, то остановить выполнение.

Что если “в некоторых случаях” все нормально?

Итак, мы запускаем код — и бинго, он останавливается в точке останова… Но, к сожалению, здесь не тот случай. Эта точка останова связана с другим списком, который мы сейчас не отлаживаем. По-видимому, в этом списке нулевое значение — в порядке вещей.

Поэтому мы нажимаем “Продолжить”, и точка останова срабатывает снова, и снова, и снова. Каждый раз — из-за неправильного списка. Зачастую именно в такие моменты разработчики начинают громко ругаться и ругать отладчики в тоске по старым-добрым журналам.

Есть несколько способов обойти эту проблему. Идеальный вариант — избегать этого конкретного списка. Если у вас есть способ распознать этот список, например, глобальный экземпляр или первый элемент с определенным значением, можно просто преобразовать исходную условную точку останова. К примеру, в рассматриваемом случае мы предполагаем, что первый элемент в списке null_is_OK равен 77, и тогда это условие позволит обойти проблему:

Не идеально, но сработает как обходной путь, если проблема достаточно хорошо локализована.

Вложенная трассировка стека

Вот она, одна из “болей” современных фреймворков. Фреймворк улавливает исключение и распространяет его, заключая в свое собственное исключение. Смыть, повторить. В итоге получается матрешка из трассировок стека.

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

Может кто-нибудь сказать, что я тут сделал не так при попытке запустить сервер локально?

Попробуем разбить эту стену текста, начиная с самого нижнего исключения, которое обычно и бывает основной причиной:

Налицо проблема с подключением. Соединение отклонено, что означает — наш сервер пытается подключиться к какому-то еще серверу, но терпит неудачу.

Так мы получили некоторую базовую информацию, но ничего больше.

Перейдем на одно исключение выше и взглянем на второй блок. Поскольку он большой, я сосредоточусь только на крайней части исключения:

И снова ничего стоящего. Это код подключения к Apache. Но никакой информации о том, кто его спровоцировал и почему.

Итак, мы вернулись на самый верх, что менее типично. И здесь мы, наконец, находим ответ:

Если посмотреть немного ниже, вы увидите пакеты org.keycloak . По сути, это означает, что я забыл запустить keycloak перед запуском сервера. Причина была хорошо скрыта в стеке и ее обнаружение требовало знания предметной области (мы пользуемся keycloak ).

Просто продолжайте читать стек — ответ обычно находится где-то с краю (внизу или вверху). Не сдавайтесь, если что-то проясняется не сразу.

Как правило, причину исключения, которое мы видим в журнале или получаем от пользователя, можно выяснить, просто поглядев на трассировку стека и копнув глубже. Само собой, держите в голове вложенные исключения и другие подобные проблемы.

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

Конец трассировки внутреннего стека исключений что это

Подробная информация об использовании оперативной

Win Xp SP3
2.4 Ггц
2.00 ОЗУ
Видео 512

  • 14 марта 2018 — 06:59
  • #1

Фото

увы, но win XP, насколько знаю, игра не поддерживает.

Последняя ошибка правится удалением с пути до папки с игрой и имени папки с игрой русских символов и пробелов

P.S. Помня репутацию сборок Zver XP, посоветовал бы ПК на вирусы проверить

  • 14 марта 2018 — 07:08
  • #2

Фото

увы, но win XP, насколько знаю, игра не поддерживает.

Последняя ошибка правится удалением с пути до папки с игрой и имени папки с игрой русских символов и пробелов

P.S. Помня репутацию сборок Zver XP, посоветовал бы ПК на вирусы проверить

Чистос вирусами, попробую убрать символы и пробелы

  • 14 марта 2018 — 07:52
  • #3

Фото

Вроде всё нормально 2 ошибка не решается

  • 14 марта 2018 — 08:01
  • #4

Фото

Вроде всё нормально 2 ошибка не решается

переназовите все папки до Launcher_sa на английском вручную, можеьт быть пакость в виде закравшегося русского в названии папки в виде буквы.
и да, параметр -winxp к Launcher_SA.exe насколько помню вроде бы бесполезно добавлять)

  • 14 марта 2018 — 08:21
  • #5

Фото

вопрос такой, иногда после рестарта ПВП сервера,не могу подключится на сервер игра просто вылетает после загрузки на сервер и так всегда, в плоть до РЕСтАРТА СЕрвера, потом может быть такая проблема, а может и нет 50-50 короче кто знает в чем проблема ?
причем если захожу на любой другой сервер, все нормально вылетов нету вообще. Именно КОГДА на ПВП СЕРВЕР

Изменено: bountyxzx, 19 марта 2018 — 03:50

  • 19 марта 2018 — 03:48
  • #6

Фото

вопрос такой, иногда после рестарта ПВП сервера,не могу подключится на сервер игра просто вылетает после загрузки на сервер и так всегда, в плоть до РЕСтАРТА СЕрвера, потом может быть такая проблема, а может и нет 50-50 короче кто знает в чем проблема ?
причем если захожу на любой другой сервер, все нормально вылетов нету вообще. Именно КОГДА на ПВП СЕРВЕР

возможно зависит от выпадаемой на сервере локации

  • 19 марта 2018 — 05:15
  • #7

Фото

возможно зависит от выпадаемой на сервере локации

все оказалось проще, как только такая ошибка просто меняю ник и вуаля больше не вылетает

  • 11 апреля 2018 — 03:14
  • #8

Фото

все оказалось проще, как только такая ошибка просто меняю ник и вуаля больше не вылетает

попробуйте папку smartsteamemu в %appdata% убивать в это время, слышал некоторым помогало

Всё, что вы хотели знать о стек-трейсах и хип-дампах. Часть 1

Практика показала, что хардкорные расшифровки с наших докладов хорошо заходят, так что мы решили продолжать. Сегодня у нас в меню смесь из подходов к поиску и анализу ошибок и крэшей, приправленная щепоткой полезных инструментов, подготовленная на основе доклада Андрея Паньгина aka apangin из Одноклассников на одном из JUG’ов (это была допиленная версия его доклада с JPoint 2016). В без семи минут двухчасовом докладе Андрей подробно рассказывает о стек-трейсах и хип-дампах.

Пост получился просто огромный, так что мы разбили его на две части. Сейчас вы читаете первую часть, вторая часть лежит здесь.

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

Когда я делал тренировочный прогон этого доклада у нас в офисе, один из коллег спросил: «Все это очень интересно, но на практике это кому-нибудь вообще полезно?» После этого разговора первым слайдом в свою презентацию я добавил страницу с вопросами по теме на StackOverflow. Так что это актуально.

Сам я работаю ведущим программистом в Одноклассниках. И так сложилось, что зачастую мне приходится работать с внутренностями Java — тюнить ее, искать баги, дергать что-то через системные классы (порой не совсем легальными способами). Оттуда я и почерпнул большую часть информации, которую сегодня хотел вам представить. Конечно, в этом мне очень помог мой предыдущий опыт: я 6 лет работал в Sun Microsystems, занимался непосредственно разработкой виртуальной Java-машины. Так что теперь я знаю эту тему как изнутри JVM, так и со стороны пользователя-разработчика.

Стек-трейсы

Стек-трейсы exception

Когда начинающий разработчик пишет свой «Hello world!», у него выскакивает эксепшн и ему демонстрируется стек-трейс, где произошла эта ошибка. Так что какие-то представления о стек-трейсах есть у большинства.

Перейдем сразу к примерам.

Я написал небольшую программку, которая в цикле 100 миллионов раз производит такой эксперимент: создает массив из 10 случайных элементов типа long и проверяет, сортированный он получился или нет.

По сути он считает вероятность получения сортированного массива, которая приблизительно равна 1/n! . Как это часто бывает, в программке ошиблись на единичку:

Что произойдет? Эксепшн, выход за пределы массива.
Давайте разбираться, в чем дело. У нас в консоль выводится:

но стек-трейсов никаких нет. Куда делись?

В HotSpot JVM есть такая оптимизация: у эксепшенов, которые кидает сама JVM из горячего кода, а в данном случае код у нас горячий — 100 миллионов раз дергается, стек-трейсы не генерируются.
Это можно исправить с помощью специального ключика:

Теперь попробуем запустить пример. Получаем все то же самое, только все стек-трейсы на месте.

Подобная оптимизация работает для всех неявных эксепшенов, которые бросает JVM: выход за границы массива, разыменование нулевого указателя и т.д.

Раз оптимизацию придумали, значит она зачем-то нужна? Понятно, что программисту удобнее, когда стек-трейсы есть.

Давайте измерим, сколько «стоит» у нас создание эксепшена (сравним с каким-нибудь простым Java-объектом, вроде Date).

Читать:
Etd sys windows 10 как исправить

С помощью JMH напишем простенькую бенчмарку и измерим, сколько наносекунд занимают обе операции.

Оказывается, создать эксепшн в 150 раз дороже, чем обычный объект.
И тут не все так просто. Для виртуальной машины эксепшн не отличается от любого другого объекта, но разгадка кроется в том, что практически все конструкторы эксепшн так или иначе сводятся к вызову метода fillInStackTrace, который заполняет стек-трейс этого эксепшена. Именно заполнение стек-трейса отнимает время.

Этот метод в свою очередь нативный, падает в VM рантайм и там гуляет по стеку, собирает все фреймы.

Метод fillInStackTrace публичный, не final. Давайте его просто переопределим:

Теперь создание обычного объекта и эксепшена без стек-трейса отнимают одинаковое время.

Есть и другой способ создать эксепшн без стек-трейса. Начиная с Java 7, у Throwable и у Exception есть protected-конструктор с дополнительным параметром writableStackTrace:

Если туда передать false, то стек-трейс генерироваться не будет, и создание эксепшена будет очень быстрым.

Зачем нужны эксепшены без стек-трейсов? К примеру, если эксепшн используется в коде в качестве способа быстро выбраться из цикла. Конечно, лучше так не делать, но бывают случаи, когда это действительно дает прирост производительности.

А сколько стоит бросить эксепшн?

Рассмотрим разные случаи: когда он бросается и ловится в одном методе, а также ситуации с разной глубиной стека.

Вот, что дают измерения:

Т.е. если у нас глубина небольшая (эксепшн ловится в том же фрейме или фреймом выше — глубина 0 или 1), эксепшн ничего не стоит. Но как только глубина стека становится большой, затраты совсем другого порядка. При этом наблюдается четкая линейная зависимость: «стоимость» исключения почти линейно зависит от глубины стека.

Дорого стоит не только получение стек-трейса, но и дальнейшие манипуляции — распечатка, отправка по сети, запись, — все, для чего используется метод getStackTrace, который переводит сохраненный стек-трейс в объекты Java.

Видно, что преобразование стек-трейса в 10 раз «дороже» его получения:

Почему это происходит?

Вот метод getStackTrace в исходниках JDK:

Сначала через вызов нативного метода мы узнаем глубину стека, потом в цикле до этой глубины вызываем нативный метод, чтобы получить очередной фрейм и сконвертировать его в объект StackTraceElement (это нормальный объект Java с кучей полей). Мало того, что это долго, процедура отнимает много памяти.

Более того, в Java 9 этот объект дополнен новыми полями (в связи с известным проектом модуляризации) — теперь каждому фрейму приписывается отметка о том, из какого он модуля.

Привет тем, кто парсит эксепшены с помощью регулярных выражений. Готовьтесь к сюрпризам в Java 9 — появятся еще и модули.

Давайте подведем итоги

    создание самого объекта эксепшн — дешевое;

Пара советов:

    отключайте на продакшене оптимизацию, возможно, это сэкономит много времени на отладке:

Стек-трейсы в тред дампах

Чтобы узнать, что же делает программа, проще всего взять тред дамп, например, утилитой jstack.

Фрагменты вывода этой утилиты:

Что здесь видно? Какие есть потоки, в каком они состоянии и их текущий стек.

Более того, если потоки захватили какие-то локи, ожидают входа в synchronized-секцию или взятия ReentrantLock, это также будет отражено в стек-трейсе.

Порой полезным оказывается малоизвестный идентификатор:

Он напрямую связан с ID потока в операционной системе. Например, если вы смотрите программой top в Linux, какие треды у вас больше всего едят CPU, pid потока — это и есть тот самый nid, который демонстрируется в тред дампе. Можно тут же найти, какому Java-потоку он соответствует.

В случае с мониторами (с synchronized-объектами) прямо в тред дампе будет написано, какой тред и какие мониторы держит, кто пытается их захватить.

В случае с ReentrantLock это, к сожалению, не так. Здесь видно, как Thread 1 пытается захватить некий ReentrantLock, но при этом не видно, кто этот лок держит. На этот случай в VM есть опция:

Если мы запустим то же самое с PrintConcurrentLocks, в тред дампе увидим и ReentrantLock.

Здесь указан тот самый id лока. Видно, что его захватил Thread 2.

Если опция такая хорошая, почему бы ее не сделать «по умолчанию»?

Она тоже чего-то стоит. Чтобы напечатать информацию о том, какой поток какие ReentrantLock’и держит, JVM пробегает весь Java heap, ищет там все ReentrantLock’и, сопоставляет их с тредами и только потом выводит эту информацию (у треда нет информации о том, какие локи он захватил; информация есть только в обратную сторону — какой лок связан с каким тредом).

В указанном примере по названиям потоков (Thread 1 / Thread 2) непонятно, к чему они относятся. Мой совет из практики: если у вас происходит какая-то длинная операция, например, сервер обрабатывает клиентские запросы или, наоборот, клиент ходит к нескольким серверам, выставляйте треду понятное имя (как в случае ниже — прямо IP того сервера, к которому клиент сейчас идет). И тогда в дампе потока сразу будет видно, ответа от какого сервера он сейчас ждет.

Хватит теории. Давайте опять к практике. Этот пример я уже не раз приводил.

Запускаем программку 3 раза подряд. 2 раза она выводит сумму чисел от 0 до 100 (не включая 100), третий — не хочет. Давайте смотреть тред дампы:

Первый поток оказывается RUNNABLE, выполняет наш reduce. Но смотрите, какой интересный момент: Thread.State вроде как RUNNABLE, но при этом написано, что поток in Object.wait().

Мне тоже это было не понятно. Я даже хотел сообщить о баге, но оказывается, такая бага заведена много лет назад и закрыта с формулировкой: «not an issue, will not fix».
В этой программке действительно есть дедлок. Его причина — инициализация классов.

Выражение выполняется в статическом инициализаторе класса ParallelSum:

Но поскольку стрим параллельный, исполнение происходит в отдельных потоках ForkJoinPool, из которых вызывается тело лямбды:

Код лямбды записан Java-компилятором прямо в классе ParallelSum в виде приватного метода. Получается, что из ForkJoinPool мы пытаемся обратиться к классу ParallelSum, который в данный момент находится на этапе инициализации. Поэтому потоки начинают ждать, когда же закончится инициализация класса, а она не может закончиться, поскольку ожидает вычисления этой самой свертки. Дедлок.

Почему вначале сумма считалась? Просто повезло. У нас небольшое количество элементов суммируется, и иногда все исполняется в одном потоке (другой поток просто не успевает).

Но почему же тогда поток в стек-трейсе RUNNABLE? Если почитать документацию к Thread.State, станет понятно, что никакого другого состояния здесь быть не может. Не может быть состояния BLOCKED, поскольку поток не заблокирован на Java-мониторе, нет никакой synchronized-секции, и не может быть состояния WAITING, потому что здесь нет никаких вызовов Object.wait(). Синхронизация происходит на внутреннем объекте виртуальной машины, который, вообще говоря, даже не обязан быть Java-объектом.

Стек-трейс при логировании

Представьте себе ситуацию: в куче мест в нашем приложении что-то логируется. Было бы полезно узнать, из какого места появилась та или иная строчка.

В Java нет препроцессора, поэтому нет возможности использовать макросы __FILE__, __LINE__, как в С (эти макросы еще на этапе компиляции преобразуются в текущее имя файла и строку). Поэтому других способов дополнить вывод именем файла и номером строки кода, откуда это было напечатано, кроме как через стек-трейсы, нет.

Генерим эксепшн, у него получаем стек-трейс, берем в данном случае второй фрейм (нулевой — это метод getLocation, а первый — вызывает метод warning).

Как мы знаем, получение стек-трейса и, тем более, преобразование его в стек-трейс элементы очень дорого. А нам нужен один фрейм. Можно ли как-то проще сделать (без эксепшн)?

Помимо getStackTrace у исключения есть метод getStackTrace объекта Thread.

Будет ли так быстрее?

Нет. JVM никакой магии не делает, здесь все будет работать через тот же эксепшн с точно таким же стек-трейсом.

Но хитрый способ все-таки есть:

Я люблю всякие приватные штуки: Unsafe, SharedSecrets и т.д.

Есть аксессор, который позволяет получить StackTraceElement конкретного фрейма (без необходимости преобразовывать весь стек-трейс в Java-объекты). Это будет работать быстрее. Но есть плохая новость: в Java 9 это работать не будет. Там проделана большая работа по рефакторингу всего, что связано со стек-трейсами, и таких методов там теперь просто нет.

Конструкция, позволяющая получить какой-то один фрейм, может быть полезна в так называемых Caller-sensitive методах — методах, чей результат может зависеть от того, кто их вызывает. В прикладных программах с такими методами приходится сталкиваться нечасто, но в самой JDK подобных примеров немало:

В зависимости от того, кто вызывает Class.forName, поиск класса будет осуществляться в соответствующем класс-лоадере (того класса, который вызвал этот метод); аналогично — с получением ResourceBundle и загрузкой библиотеки System.loadLibrary. Также информация о том, кто вызывает, полезна при использовании различных методов, которые проверяют пермиссии (а имеет ли данный код право вызывать этот метод). На этот случай в «секретном» API предусмотрен метод getCallerClass, который на самом деле является JVM-интринсиком и вообще почти ничего не стоит.

Как уже много раз говорилось, приватный API — это зло, использовать которое крайне не рекомендуется (сами рискуете нарваться на проблемы, подобные тем, что ранее вызвал Unsafe). Поэтому разработчики JDK задумались над тем, что раз этим пользуются, нужна легальная альтернатива — новый API для обхода потоков. Основные требования к этому API:

    чтобы можно было обойти только часть фреймов (если нам нужно буквально несколько верхних фреймов);

    опция RETAIN_CLASS_REFERENCE означает, что вам нужны не имена классов, а именно инстансы;

Простейший пример, как этим пользоваться:

Берем StackWalker и вызываем метод forEach, чтобы он обошел все фреймы. В результате получим такой простой стек-трейс:

То же самое с опцией SHOW_REFLECT_FRAMES:

В этом случае добавятся методы, относящиеся к вызову через рефлекшн:

Если добавить опцию SHOW_HIDDEN_FRAMES (она, кстати, включает в себя SHOW_REFLECT_FRAMES, т.е. рефлекшн-фреймы тоже будут показаны):

В стек-трейсе появятся методы динамически-сгенерированных классов лямбд:

А теперь самый главный метод, который есть в StackWalker API — метод walk с такой хитрой непонятной сигнатурой с кучей дженериков:

Метод walk принимает функцию от стек-фрейма.

Его работу проще показать на примере.

Несмотря на то, что все это выглядит страшно, как этим пользоваться — очевидно. В функцию передается стрим, а уже над стримом можно проводить все привычные операции. К примеру, вот так выглядел бы метод getCallerFrame, который достает только второй фрейм: пропускаются первые 2, потом вызывается findFirst:

Метод walk возвращает тот результат, который возвращает эта функция стрима. Все просто.
Для данного конкретного случая (когда нужно получить просто Caller класс) есть специальный shortcut метод:

Еще один пример посложнее.

Обходим все фреймы, оставляем только те, которые относятся к пакету org.apache, и выводим первые 10 в список.

Интересный вопрос: зачем такая длинная сигнатура с кучей дженериков? Почему бы просто не сделать у StackWalker метод, который возвращает стрим?

Если дать API, который возвращает стрим, у JDK теряется контроль над тем, что дальше над этим стримом делают. Можно дальше этот стрим положить куда-то, отдать в другой поток, попробовать его использовать через 2 часа после получения (тот стек, который мы пытались обойти, давно потерян, а тред может быть давно убит). Таким образом будет невозможно обеспечить «ленивость» Stack Walker API.

Основной поинт Stack Walker API: пока вы находитесь внутри walk, у вас зафиксировано состояние стека, поэтому все операции на этом стеке можно делать lazy.

На десерт еще немного интересного.

Как всегда, разработчики JDK прячут от нас кучу сокровищ. И помимо обычных стек-фреймов они для каких-то своих нужд сделали живые стек-фреймы, которые отличаются от обычных тем, что имеют дополнительные методы, позволяющие не только получить информацию о методе и классе, но еще и о локальных переменных, захваченных мониторах и значениях экспрешн-стека данного стек-фрейма.

Защита здесь не ахти какая: класс просто сделали непубличным. Но кто же нам мешает взять рефлекшн и попробовать его? (Примечание: в актуальных сборках JDK 9 доступ к непубличному API через рефлекшн запрещён. Чтобы его разрешить, необходимо добавить опцию JVM —add-opens=java.base/java.lang=ALL-UNNAMED )

Пробуем на таком примере. Есть программа, которая рекурсивным методом ищет выход из лабиринта. У нас есть квадратное поле size x size. Есть метод visit с текущими координатами. Мы пытаемся из текущей клетки пойти влево / вправо / вверх / вниз (если они не заняты). Если дошли из правой-нижней клетки в левую-верхнюю, считаем, что нашли выход и распечатываем стек.

Если я делаю обычный dumpStack, который был еще в Java 8, получаем обычный стек-трейс, из которого ничего не понятно. Очевидно — рекурсивный метод сам себя вызывает, но интересно, на каком шаге (и с какими значениями координат) вызывается каждый метод.

Заменим стандартный dumpStack на наш StackTrace.dump, который через рефлекшн использует live стек-фреймы:

В первую очередь надо получить соответствующий StackWalker, вызвав метод getStackWalker. Все фреймы, которые будут передаваться в getStackWalker, на самом деле будут экземплярами лайв стек-фрейма, у которого есть дополнительные методы, в частности, getLocals для получения локальных переменных.

Запускаем. Получаем то же самое, но у нас отображается весь путь из лабиринта в виде значений локальных переменных:

На этом мы заканчиваем первую часть поста. Вторая часть здесь.

Лично встретиться с Андреем в Москве можно будет уже совсем скоро — 7-8 апреля на JPoint 2017. В этот раз он выступит с докладом «JVM-профайлер с чувством такта», в котором расскажет, как можно получить честные замеры производительности приложения, комбинируя несколько подходов к профилированию. Доклад будет «всего» часовой, зато в дискуссионной зоне никто не будет ограничивать вас от вопросов и горячих споров!

Кроме этого, на JPoint есть целая куча крутых докладов практически обо всем из мира Java — обзор планируемых докладов мы давали в другом посте, а просто программу конференции вы найдете на сайте мероприятия.

Похожие статьи