Какую информацию можно получить из stacktraceelement java
Элемент в трассировке стека, как возвращено Throwable.getStackTrace() . Каждый элемент представляет единственный стековый фрейм. Все стековые фреймы за исключением того наверху стека представляют вызов метода. Фрейм наверху стека представляет точку выполнения, в которой была сгенерирована трассировка стека. Как правило, это — точка, в которой создавалось соответствие throwable трассировке стека.
Сводка конструктора
Создает микроэлемент стека, представляющий указанную точку выполнения.
Сводка метода
| Модификатор и Тип | Метод и Описание |
|---|---|
| boolean | equals(Object obj) |
Возвращает true, если указанный объект — другой StackTraceElement экземпляр, представляющий ту же самую точку выполнения как этот экземпляр.
Возвращает полностью определенное имя класса, содержащего точку выполнения, представленную этим микроэлементом стека.
Возвращает имя исходного файла, содержащего точку выполнения, представленную этим микроэлементом стека.
Возвращает номер строки исходной строки, содержащей точку выполнения, представленную этим микроэлементом стека.
Возвращает имя метода, содержащего точку выполнения, представленную этим микроэлементом стека.
Возвращает значение хэш-кода для этого микроэлемента стека.
Возвращает true, если метод, содержащий точку выполнения, представленную этим микроэлементом стека, является собственным методом.
Возвращает строковое представление этого микроэлемента стека.
Методы java.lang унаследованный от класса. Объект
Деталь конструктора
StackTraceElement
public StackTraceElement(String declaringClass, String methodName, String fileName, int lineNumber)
Создает микроэлемент стека, представляющий указанную точку выполнения.
Деталь метода
getFileName
public String getFileName()
Возвращает имя исходного файла, содержащего точку выполнения, представленную этим микроэлементом стека. Обычно, это соответствует SourceFile атрибут соответствующего class файл (согласно Спецификации виртуальной машины Java, Раздел 4.7.7). В некоторых системах имя может обратиться к некоторому модулю исходного кода кроме файла, такого как запись в исходном репозитарии.
getLineNumber
public int getLineNumber()
Возвращает номер строки исходной строки, содержащей точку выполнения, представленную этим микроэлементом стека. Обычно, это получается из LineNumberTable атрибут соответствующего class файл (согласно Спецификации виртуальной машины Java, Раздел 4.7.8).
getClassName
public String getClassName()
Возвращает полностью определенное имя класса, содержащего точку выполнения, представленную этим микроэлементом стека.
getMethodName
public String getMethodName()
Возвращает имя метода, содержащего точку выполнения, представленную этим микроэлементом стека. Если точка выполнения будет содержаться в экземпляре или инициализаторе класса, то этот метод возвратит соответствующее специальное имя метода, или , согласно Разделу 3.9 из Спецификации виртуальной машины Java.
isNativeMethod
public boolean isNativeMethod()
Возвращает true, если метод, содержащий точку выполнения, представленную этим микроэлементом стека, является собственным методом.
toString
public String toString()
- «MyClass.mash(MyClass.java:9)» — Здесь, «MyClass» полностью определенное имя класса, содержащего точку выполнения, представленную этим микроэлементом стека, «mash» имя метода, содержащего точку выполнения, «MyClass.java» исходный файл, содержащий точку выполнения, и «9» номер строки исходной строки, содержащей точку выполнения.
- «MyClass.mash(MyClass.java)» — Как выше, но номер строки недоступно.
- «MyClass.mash(Unknown Source)» — Как выше, но ни имя файла, ни номер строки не доступны.
- «MyClass.mash(Native Method)» — Как выше, но ни имя файла, ни номер строки не доступны, и метод, содержащий точку выполнения, как известно, является собственным методом.
равняется
Возвращает true, если указанный объект — другой StackTraceElement экземпляр, представляющий ту же самую точку выполнения как этот экземпляр. Два микроэлемента стека a и b равны если и только если:
equals(a.getFileName(), b.getFileName()) && a.getLineNumber() == b.getLineNumber()) && equals(a.getClassName(), b.getClassName()) && equals(a.getMethodName(), b.getMethodName())
где equals имеет семантику Objects.equals .
хэш-код
public int hashCode()
Элементы трассировки стека
Стандартная библиотека Java имеет механизм для отображения трассировки стека, использующий метод Throwable.printStackTrace . Этот метод используется для создания дампа контекста программы для не перехваченных исключительных ситуаций. Информация о трассировке выводится в поток System.err или в указанный PrintStream или PrintWriter .
Новые возможности библиотеки предоставляют вам возможность программного доступа к трассировке стека. Вы можете извлечь массив объектов StackTraceElement , каждый объект представляет единичный фрейм стека в трассировочной информации.
Пример работы стека
Новые возможности библиотеки предоставляют вам возможность программного доступа к трассировке стека. Вы можете извлечь массив объектов StackTraceElement , каждый объект представляет единичный фрейм стека в трассировочной информации. Рассмотрим пример, чтобы понять, как это работает:
class C String str;
int len = str.length () ;
>
public class TraceDemo1
// создать дамп единичного элемента трассировки стека
static void dumpTraceElement ( StackTraceElement ste ) System.err.println ( «filename = » + ste.getFileName ()) ;
System.err.println ( «line number = » + ste.getLineNumber ()) ;
System.err.println ( «class name = » + ste.getClassName ()) ;
System.err.println ( «method name = » + ste.getMethodName ()) ;
System.err.println ( «is native method = » + ste.isNativeMethod ()) ;
>
// создать дамп массива элементов трассировки стека,
// сначала самые последние по времени
static void dumpTrace ( Throwable e )
// отобразить исключительную ситуацию
System.err.println ( «Exception: » + e ) ;
System.err.println () ;
StackTraceElement ste [] = e.getStackTrace () ;
for ( int i = 0 ; i < ste.length; i++ ) dumpTraceElement ( ste [ i ]) ;
System.err.println () ;
>
>
public static void main ( String args [])
// вызвать A.f() и инициировать исключительную ситуацию
try A aref = new A () ;
aref.f () ;
> catch ( Throwable e )
// отобразить типовую трассировку стека
// создать дамп трассировки стека в пользовательском формате
System.err.println () ;
System.err.println ( «==============================» ) ;
System.err.println () ;
// вызвать исключительную ситуацию при инициализации
try new C () ;
> catch ( Throwable e ) dumpTrace ( e ) ;
>
>
>
Исключительная ситуация: ссылка имеет значение null
В этом примере программа TraceDemo1 сначала вызывает метод A.f . Этот метод, в свою очередь, вызывает B.g . К сожалению, при вызове B.g ссылка на объект B имеет значение null . Это вызывает исключительную ситуацию.
Сначала программа отображает типовую трассировку стека, которая выглядит примерно так:
java.lang.NullPointerException
at A.f ( TraceDemo1.java: 5 )
at TraceDemo1.main ( TraceDemo1.java: 61 )
Затем отображается пользовательскую трассировку стека — с названием исключительной ситуации и последовательностью элементов StackTraceElements:
Exception: java.lang.NullPointerException filename = TraceDemo1.java line number = 5 class name = A method name = f is native method = false filename = TraceDemo1.java line number = 61 class name = TraceDemo1 method name = main is native method = false
Обратите внимание, что имя файла, имя класса и имя метода первого StackTraceElement ссылается на исключительную ситуацию. Исключительная ситуация происходит в строке 5 программы TraceDemo1.java внутри метода A.f .
Исключительная ситуация генерируется во время инициализации экземпляра
Вторая часть примера показывает, что происходит, когда исключительная ситуация генерируется во время инициализации экземпляра. В этом примере программа TraceDemo1 создает объект C. При создании экземпляра объекта C делается попытка получить длину строки str . Однако, поскольку str никогда явно не инициализировалась, ссылка на нее равна null . Вот результат работы этой части программы:
Exception: java.lang.NullPointerException filename = TraceDemo1.java line number = 15 class name = C method name = is native method = false filename = TraceDemo1.java line number = 83 class name = TraceDemo1 method name = main is native method = false
Точка входа в исключительную ситуацию расположена в строке 15 программы TraceDemo1.java в классе C в методе с именем , являющимся специальным именем для методов инициализации экземпляров в виртуальной машине Java.
Вы можете использовать способность StackTraceElement реализовывать форматированные отчеты об исключительных ситуациях и собственные форматы журналов. Примером пользовательского формата ведения журнала является формат, ограничивающий трассировку стека до разумной глубины. Например, если есть 500 фреймов стека, вы, возможно, захотите сохранить первые десять и последние десять. Информация StackTraceElement является частью сериализованного представления экземпляров класса Throwable , поэтому она доступна для десериализованных объектов.
Необычная Java: StackTrace Extends Throwable
Есть вещи, которые вы можете делать в Java, но вы их редко видите. В основном потому, что в них нет смысла. Однако в Java есть несколько необычных вещей, которые могут оказаться на удивление полезными.
Chronicle Software обычно использует ряд различных шаблонов в своих низкоуровневых библиотеках, с которыми большинство разработчиков вообще не сталкивается.
Один из них — это класс, который расширяет Throwable, но не является ошибкой или исключением.
StackTrace Extends Throwable
package net.openhft.chronicle.core; /** * Throwable created purely for the purposes of reporting a stack trace. * This is not an Error or an Exception and is not expected to be thrown or caught. */ public class StackTrace extends Throwable < public StackTrace() < this("stack trace"); >public StackTrace(String message) < this(message, null); >public StackTrace(String message, Throwable cause) < super(message + " on " + Thread.currentThread().getName(), cause); >public static StackTrace forThread(Thread t) < if (t == null) return null; StackTrace st = new StackTrace(t.toString()); StackTraceElement[] stackTrace = t.getStackTrace(); int start = 0; if (stackTrace.length >2) < if (stackTrace[0].isNativeMethod()) < start++; >> if (start > 0) < StackTraceElement[] ste2 = new StackTraceElement[stackTrace.length - start]; System.arraycopy(stackTrace, start, ste2, 0, ste2.length); stackTrace = ste2; >st.setStackTrace(stackTrace); return st; > >
Некоторые важные примечания, чтобы для начала:
- Это не тот класс исключения, которое, как я рассчитываю, когда-либо может возникнуть. Классы, непосредственно расширяющие Throwable, проверяются, как и Exception, поэтому компилятор поможет вам обеспечить эту проверку.
- Трассировка стека Throwable определяется при создании Throwable, а не там, где она возникает. Обычно это одна и та же строка, но это не обязательно. Чтобы получить трассировку стека, Throwable не должно вызвать исключение.
- Объекты элементов трассировки стека не создаются до тех пор, пока они не потребуются. Вместо этого метаданные добавляются к самому объекту, чтобы уменьшить накладные расходы, а массив StackTraceElements заполняется при первом использовании.
Однако давайте рассмотрим этот класс более подробно. Класс будет протоколировать как трассировку стека, где он был создан, так и поток, который его создал. Позже вы увидите насколько это полезно.
Этот класс также можно использовать для хранения трассировки стека другого запущенного потока.
Трассировка стека другого потока берется только тогда, когда поток достигает безопасной точки. Это может произойти через некоторое время после того, как вы попытаетесь получить его.
Связано это с тем, что JVM останавливает поток, и обычно JVM ждут, чтобы остановить каждый поток, поэтому он может проверить стек потока, который вы пытаетесь захватить. Т.е. это имеет высокие накладные расходы, но может быть очень полезным.
StackTrace как отложенное исключение
Мы не предполагаем, что этот Throwable класс будет вызван, но он может записать причину исключения, которое может быть вызвано позже.
Почему был закрыт ресурс
public class EgMain < static class MyCloseable implements Closeable < protected transient volatile StackTrace closedHere; @Override public void close() < closedHere = new StackTrace("Closed here"); // line 13 >public void useThis() < if (closedHere != null) throw new IllegalStateException("Closed", closedHere); >> public static void main(String[] args) throws InterruptedException < MyCloseable mc = new MyCloseable(); // line 27 Thread t = new Thread(mc::close, "closer"); t.start(); t.join(); mc.useThis(); >>
При запуске код выдает следующее исключение:

Обычно вы увидите исключение IllegalStateException и место, где ваш код пытался использовать закрытый ресурс, но это не говорит вам, почему он был закрыт без дополнительной информации.
Поскольку StackTrace является Throwable классом, вы можете указать его в качестве причины последующего исключения или ошибки.
Вы можете увидеть поток, который закрыл ресурс. Таким образом вы узнаете, что это исключение или ошибка произошло в другом потоке. И вы можете увидеть трассировку стека с причиной, по которой он был закрыт. Это может помочь очень быстро диагностировать трудно обнаруживаемые проблемы связанные с преждевременным закрытием ресурсов.
Какой ресурс был закрыт?
Долгоживущие объекты Closeable могут иметь сложный жизненный цикл, и обеспечение их закрытия, когда это необходимо, может быть трудно отследить. Что может привести к утечке ресурсов.
Некоторые ресурсы не удаляются, когда GC освобождает объект, например, объект RandomAccessFile освобождается в GC вместе с файлом, который он представляет, и не закрывается, пока вы его не закроете, что приводит к потенциальной утечке ресурсов файловых дескрипторов.
public class CreatedMain < static class MyResource implements Closeable < private final transient StackTrace createdHere = new StackTrace("Created here"); volatile transient boolean closed; @Override public void close() throws IOException < closed = true; >@Override protected void finalize() throws Throwable < super.finalize(); if (!closed) Logger.getAnonymousLogger().log(Level.WARNING, "Resource discarded but not closed", createdHere); >> public static void main(String[] args) throws InterruptedException < new MyResource(); // line 27 System.gc(); Thread.sleep(1000); >>
Код выводит следующее:

Что позволяет вам не только видеть, где был создан ресурс, что дает возможность определить, почему он не был закрыт, но и просто вести лог так, как понимает ваша IDE. Это возможно потому, что ваш объект Logger будет поддерживать выдачу трассировки стека. например, вы можете кликнуть по номеру строки, чтобы просмотреть код, создавший ее.
Мониторинг производительности критического потока в рабочей среде
В некоторых средах вам нужен способ мониторинга критических событий в рабочей среде с низкими издержками без запуска профилировщика.
Этого можно добиться, включив свой собственный мониторинг, чтобы производить трассировку стека только тогда, когда превышен некоторый порог. Это поможет найти проблемы, которые вы не можете воспроизвести в среде тестирования или разработки, поэтому это может быть бесценно.
После того как мы добавили эту возможность в нашу инфраструктуру, количество мистических задержек, о которых сообщили нам наши клиенты, резко сократилось, поскольку клиенты могли сами диагностировать проблему по трассировке стека.
public class JitteryMain implements Runnable < volatile long loopStartMS = Long.MIN_VALUE; volatile boolean running = true; @Override public void run() < while (running) < loopStartMS = System.currentTimeMillis(); doWork(); loopStartMS = Long.MIN_VALUE; >> private void doWork() < int loops = new Random().nextInt(100); for (int i = 0; i < loops; i++) pause(1); // line 24 >static void pause(int ms) < try < Thread.sleep(ms); // line 29 >catch (InterruptedException e) < throw new AssertionError(e); // shouldn't happen >> public static void main(String[] args) < final JitteryMain jittery = new JitteryMain(); Thread thread = new Thread(jittery, "jitter"); thread.setDaemon(true); thread.start(); // monitor loop long endMS = System.currentTimeMillis() + 1_000; while (endMS >System.currentTimeMillis()) < long busyMS = System.currentTimeMillis() - jittery.loopStartMS; if (busyMS >100) < Logger.getAnonymousLogger() .log(Level.INFO, "Thread spent longer than expected here, was " + busyMS + " ms.", StackTrace.forThread(thread)); >pause(50); > jittery.running = false; > >
Выводит следующее и опять же, как вы видите, можно легко перемещается по стеку в вашей IDE.

Вам может быть интересно, почему это происходит в данном случае. Наиболее вероятная причина заключается в том, что Thread.sleep(time) спит в течение минимального, а не максимального времени, а в Windows спящий режим 1 мс на самом деле довольно стабильно занимает около 1,9 мс.
Обнаружение одновременного доступа к однопоточному ресурсу разными потоками
package net.openhft.chronicle.core; public class ConcurrentUsageMain < static class SingleThreadedResource < private StackTrace usedHere; private Thread usedByThread; public void use() < checkMultithreadedAccess(); // BLAH >private void checkMultithreadedAccess() < if (usedHere == null || usedByThread == null) < usedHere = new StackTrace("First used here"); usedByThread = Thread.currentThread(); >else if (Thread.currentThread() != usedByThread) < throw new IllegalStateException("Used two threads " + Thread.currentThread() + " and " + usedByThread, usedHere); >> > public static void main(String[] args) throws InterruptedException < SingleThreadedResource str = new SingleThreadedResource(); final Thread thread = new Thread(() ->str.use(), "Resource user"); // line 25 thread.start(); thread.join(); str.use(); // line 29 > >

Вы можете заметить, что ресурс использовался двумя потоками с разными именами, однако вы также можете видеть, где в стеке они использовались, чтобы определить возможную причину.
Отключение этой трассировки
Создание StackTrace оказывает значительное влияние на поток и, возможно, на JVM. Однако его легко отключить с помощью управляющего флага, такого как системное свойство, и заменить его нулевым значением.
createdHere = Jvm.isResourceTracing() ? new StackTrace(getClass().getName() + " created here") : null;
Это использование значения null не требует специальной обработки, поскольку logger будет игнорировать Throwable, который имеет значение null, и вы можете указать null как причину для исключения, и это то же самое, что и не указывать ее.
Заключение
Хотя класс, который напрямую расширяет Throwable, выглядит странно, он разрешен.
Более того он удивительно полезен для предоставления дополнительной информации о жизненном цикле ресурса или добавления простого мониторинга, который вы можете запустить в рабочей среде.
Помогите разобраться с StackTraceElement[]
Java-машина ведет запись всех вызовов функций. У нее есть для этого специальная коллекция – стек. Когда одна функция вызывает другую, Java-машина помещает в этот стек новый элемент StackTraceElement. Когда функция завершается этот элемент удаляется из стека. Таким образом, в этом стеке всегда хранится актуальная информация о текущем состоянии «стека вызовов функций»
Далее приводится пример
public static void main(String[] args) < method1(); >public static void method1() < method2(); >public static void method2() < method3(); >public static void method3() < StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); for (StackTraceElement element : stackTraceElements) < System.out.println(element.getMethodName()); >>
В результате выполнения которого мы получаем
getStackTrace method3 method2 method1 main
Если я правильно все понимаю, то конструкция
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
работает следующим образом:
//Cоздаём переменную типа StackTraceElement[] StackTraceElement[] stackTraceElements //Присваеваем ей ссылку на текущий поток и заносим в массив специальные элементы StackTrace Thread.currentThread().getStackTrace()
Вопрос №1: каким образом в этом массиве StackTraceElement[] вся информация о вызываемых методах, если сказано, что после выполнения метода информация из стека удаляется? Вопрос №2: В одном из заданий просят вернуть имя метода, который его вызывает
public static void main(String[] args) throws Exception < method1(); >public static String method1() < method2(); return Thread.currentThread().getStackTrace()[2].getMethodName(); >public static String method2() < method3(); return Thread.currentThread().getStackTrace()[2].getMethodName(); >public static String method3() < method4(); return Thread.currentThread().getStackTrace()[2].getMethodName(); >public static String method4() < method5(); return Thread.currentThread().getStackTrace()[2].getMethodName(); >public static String method5()
Каким образом работает конструкция
return Thread.currentThread().getStackTrace()[2].getMethodName();
если мы не создавали StackTraceElement[] . Значит ли это, что он неявно создается JVM при выполнении программы?