Úvod do Java 9 StackWalking API

1. Úvod

V tomto rýchlom článku sa pozrieme na rozhranie StackWalking API Java 9.

Nová funkcionalita poskytuje prístup k a Prúd z StackFrames, čo nám umožňuje jednoduché prechádzanie zásobníkov v oboch priamo a dobré využitie výkonných Prúd API v Jave 8.

2. Výhody a StackWalker

V prostredí Java 8 sa Hádzateľné :: getStackTrace a Vlákno :: getStackTrace vráti pole StackTraceElements. Bez veľkého množstva manuálneho kódu neexistoval spôsob, ako zahodiť nechcené rámy a ponechať si iba tie, ktoré nás zaujímajú.

Okrem toho Vlákno :: getStackTrace môže vrátiť čiastočné trasovanie zásobníka. Je to tak preto, lebo špecifikácia umožňuje implementácii VM kvôli výkonu vynechať niektoré rámce zásobníka.

V prostredí Java 9 pomocou chodiť () metóda StackWalker, môžeme prekonať niekoľko snímok, ktoré nás zaujímajú alebo úplné sledovanie zásobníka.

Nová funkčnosť je samozrejme bezpečná pre vlákna; toto umožňuje zdieľanie jedného vlákna viacerým vláknam StackWalker napríklad pre prístup k ich príslušným komínom.

Ako je opísané v JEP-259, JVM bude vylepšený tak, aby v prípade potreby umožňoval efektívny lenivý prístup k ďalším rámcom zásobníka.

3. StackWalker v akcii

Začnime vytvorením triedy obsahujúcej reťazec volaní metód:

verejná trieda StackWalkerDemo {public void methodOne () {this.methodTwo (); } public void methodTwo () {this.methodThree (); } public void methodThree () {// chodiaci kód zásobníka}}

3.1. Zachyťte celú stopovú stopu

Poďme ďalej a pridajme nejaký chodiaci kód zásobníka:

public void methodThree () {List stackTrace = StackWalker.getInstance () .walk (this :: walkExample); } 

The StackWalker :: chôdza metóda prijíma funkčný odkaz, vytvára a Prúd z StackFrames pre aktuálne vlákno, použije funkciu na Prúda zatvorí Prúd.

Teraz definujme StackWalkerDemo :: walkExample metóda:

public List walkExample (Stream stackFrameStream) {return stackFrameStream.collect (Collectors.toList ()); }

Táto metóda jednoducho zhromažďuje StackFrames a vráti ju ako a Zoznam. Ak chcete otestovať tento príklad, spustite test JUnit:

@ Test public void giveStalkWalker_whenWalkingTheStack_thenShowStackFrames () {new StackWalkerDemo (). MethodOne (); }

Jediným dôvodom, prečo ho spustiť ako test JUnit, je mať v našom zásobníku viac rámcov:

trieda com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, riadok 20, trieda com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, riadok 15, trieda com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, riadok 11, trieda com.baeldung. java9.stackwalker .StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, riadok 9, trieda org.junit.runners.model.FrameworkMethod $ 1 # runReflectiveCall, riadok 50, trieda org.junit.internal.runners.model.Refners.model.Runners.model.Runners.model.Runners.model. rámy ... trieda org.junit.runners.ParentRunner # beh, riadok 363 trieda org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference # beh, riadok 86 ... viac org.eclipse rámcov ... trieda org. eclipse.jdt.internal.junit.runner.RemoteTestRunner # main, riadok 192

V celom sledovaní zásobníka nás zaujímajú iba horné štyri snímky. Zostávajúce rámy z org.junit a org.eclipse nie sú nič iné ako rámce hluku.

3.2. Filtrovanie StackFrames

Poďme vylepšiť náš chodiaci kód a odstrániť hluk:

public List walkExample2 (Stream stackFrameStream) {return stackFrameStream .filter (f -> f.getClassName (). contains ("com.baeldung")) .collect (Collectors.toList ()); }

Využitie sily Prúd API, ponechávame iba tie rámce, ktoré nás zaujímajú. Toto vymaže hluk a ponechá horné štyri riadky v protokole zásobníka:

trieda com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, riadok 27, trieda com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, riadok 15, trieda com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, riadok 11, trieda com.baeldung. java9.stackwalker .StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, riadok 9

Poďme teraz určiť test JUnit, ktorý inicioval hovor:

public String walkExample3 (Stream stackFrameStream) {return stackFrameStream .filter (frame -> frame.getClassName () .contains ("com.baeldung") && frame.getClassName (). endsWith ("Test")) .findFirst () .map (f -> f.getClassName () + "#" + f.getMethodName () + ", linka" + f.getLineNumber ()) .orElse ("neznámy volajúci"); }

Upozorňujeme, že tu nás zaujíma iba jeden StackFrame, ktorý je mapovaný na a String. Výstupom bude iba riadok obsahujúci StackWalkerDemoTest trieda.

3.3. Zachytenie odrazových rámcov

Na zachytenie odrazových rámcov, ktoré sú v predvolenom nastavení skryté, slúži značka StackWalker je potrebné nakonfigurovať s ďalšou možnosťou SHOW_REFLECT_FRAMES:

Zoznam stackTrace = StackWalker .getInstance (StackWalker.Option.SHOW_REFLECT_FRAMES) .walk (this :: walkExample);

Pri použití tejto možnosti sú všetky odrazové rámy vrátane Method.invoke () a Constructor.newInstance () bude zajatý:

com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, riadok 40 com.baeldung.java9.stackwalker.StackWalkerDemo # methodTwo, riadok 16 com.baeldung.java9.stackwalker.StackWalkerDemo # methodOne, riadok 12 com.baeldung.java9.stackwalker. StackWalkerDemoTest # giveStalkWalker_whenWalkingTheStack_thenShowStackFrames, riadok 9 jdk.internal.reflect.NativeMethodAccessorImpl # invoke0, riadok -2 jdk.internal.reflect.NativeMethodAccessorImpl # line.date.lf.int.int #invoke, riadok 547 org.junit.runners.model.FrameworkMethod $ 1 # runReflectiveCall, riadok 50 ... rámy zatmenia a junit ... org.eclipse.jdt.internal.junit.runner.RemoteTestRunner # main, riadok 192

Ako vidíme, jdk.vnútorné snímky sú nové, ktoré zachytil SHOW_REFLECT_FRAMES možnosť.

3.4. Zachytenie skrytých rámov

Okrem odrazových rámcov sa môže implementácia JVM rozhodnúť skryť rámce špecifické pre implementáciu.

Tieto rámy však nie sú skryté pred StackWalker:

Spustiteľné r = () -> {List stackTrace2 = StackWalker .getInstance (StackWalker.Option.SHOW_HIDDEN_FRAMES) .walk (this :: walkExample); printStackTrace (stackTrace2); }; r.run ();

Upozorňujeme, že priradíme lambda odkaz k a Spustiteľné v tomto príklade. Jediným dôvodom je, že JVM vytvorí nejaké skryté rámce pre výraz lambda.

Toto je jasne viditeľné v sledovaní zásobníka:

com.baeldung.java9.stackwalker.StackWalkerDemo # lambda $ 0, riadok 47 com.baeldung.java9.stackwalker.StackWalkerDemo $$ Lambda $ 39/924477420 # run, riadok -1 com.baeldung.java9.stackwalker.StackWalkerDemo # methodThree, riadok 50 com.baeldung.java9. invoke0, riadok -2 jdk.internal.reflect.NativeMethodAccessorImpl # invoke, riadok 62 jdk.internal.reflect.DelegatingMethodAccessorImpl # invoke, riadok 43 java.lang.reflect.Method # invoke, riadok 547 org.junit.runners.model.Frame $ 1 # runReflectiveCall, riadok 50 ... rámy junit a eclipse ... org.eclipse.jdt.internal.junit.runner.RemoteTestRunner # main, riadok 192

Horné dva rámce sú rámce proxy lambda, ktoré JVM vytvoril interne. Je potrebné poznamenať, že odrazové rámy, ktoré sme zachytili v predchádzajúcom príklade, sú stále zachované SHOW_HIDDEN_FRAMES možnosť. To je preto, že SHOW_HIDDEN_FRAMES je nadmnožina súboru SHOW_REFLECT_FRAMES.

3.5. Identifikácia triedy volajúceho

Možnosť RETAIN_CLASS_REFERENCE predáva predmet Trieda vo všetkých StackFrameišiel okolo StackWalker. To nám umožňuje zavolať metódy StackWalker :: getCallerClass a StackFrame :: getDeclaringClass.

Poďme určiť volajúcu triedu pomocou StackWalker :: getCallerClass metóda:

public void findCaller () {Class caller = StackWalker .getInstance (StackWalker.Option.RETAIN_CLASS_REFERENCE) .getCallerClass (); System.out.println (caller.getCanonicalName ()); }

Tentokrát túto metódu zavoláme priamo zo samostatného testu JUnit:

@ Test public void giveStalkWalker_whenInvokingFindCaller_thenFindCallingClass () {new StackWalkerDemo (). FindCaller (); }

Výstup z caller.getCanonicalName (), bude:

com.baeldung.java9.stackwalker.StackWalkerDemoTest

Upozorňujeme, že StackWalker :: getCallerClass by sa nemalo volať metódou v spodnej časti stohu. ako to bude mať za následok IllegalCallerException byť vyhodený.

4. Záver

V tomto článku sme videli, aké ľahké je sa s tým vyrovnať StackFrames využitím sily StackWalker v kombinácii s Prúd API.

Samozrejme môžeme preskúmať aj ďalšie funkcionality - napríklad preskočenie, odhodenie a obmedzenie StackFrames. Oficiálna dokumentácia obsahuje niekoľko spoľahlivých príkladov ďalších prípadov použitia.

A ako vždy môžete získať kompletný zdrojový kód pre tento článok na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found