Java
 Computer >> コンピューター >  >> プログラミング >> Java

JavaのStackWalker APIを使ってさまざまなスタックフレームを出力する方法

StackWalker APIとは

Java 9では、遅延評価(レイジー処理)とフレームフィルタリングを提供するStackWalker APIが定義されました。StackWalkerオブジェクトを使用することで、スタックの走査やアクセスが可能になり、特に有用なメソッドとしてwalk()が用意されています。

walk()メソッドは、現在のスレッドに対してStackFrameのストリームを開き、そのStackFrameストリームに関数を適用します。StackWalkerオブジェクトを取得するには、StackWalker.getInstance()メソッドを使用します。

以下の例では、StackWalker APIを使って、次の3つのパターンでスタックフレームを出力する方法を紹介します。

  • すべてのスタックフレームを出力する
  • 一部のスタックフレームをスキップする
  • 出力するスタックフレームの数を制限(limit)する

コード例

import java.lang.StackWalker.StackFrame;
import java.util.*;
import java.util.stream.*;

public class StackWalkerTest {
    public static void main(String args[]) {
        new StackWalkerTest().walk();
    }
    private void walk() {
        new Walker1().walk();
    }
    private class Walker1 {
        public void walk() {
            new Walker2().walk();
        }
    }
    private class Walker2 {
        public void walk() {
            Method1();
        }
        void Method1() {
            Method2();
        }
        void Method2() {
            // オプションと推定スタック深度を指定してインスタンスを取得
            StackWalker stackWalker = StackWalker.getInstance(Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE, StackWalker.Option.SHOW_HIDDEN_FRAMES), 16);
            Stream<StackFrame> stackStream = StackWalker.getInstance().walk(f -> f);

            System.out.println("--- Walk all StackFrames ---");
            List<String> stacks = walkAllStackframes();
            System.out.println(stacks);

            System.out.println("--- Skip some StackFrames ---");
            List<String> stacksAfterSkip = walkSomeStackframes(3);
            System.out.println(stacksAfterSkip);

            System.out.println("--- Limit StackFrames ---");
            List<String> stacksByLimit = walkLimitStackframes(3);
            System.out.println(stacksByLimit);
        }
        private List<String> walkAllStackframes() {
            return StackWalker.getInstance().walk(s -> s.map(frame -> "\n" + frame.getClassName() + "/" + frame.getMethodName()).collect(Collectors.toList()));
        }
        private List<String> walkSomeStackframes(int numberOfFrames) {
            return StackWalker.getInstance().walk(s -> s.map(frame -> "\n" + frame.getClassName() + "/" + frame.getMethodName()).skip(numberOfFrames).collect(Collectors.toList()));
        }
        private List<String> walkLimitStackframes(int numberOfFrames) {
            return StackWalker.getInstance().walk(s -> s.map(frame -> "\n" + frame.getClassName() + "/" + frame.getMethodName()).limit(numberOfFrames).collect(Collectors.toList()));
        }
    }
}

実行結果

--- Walk all StackFrames ---
[
StackWalkerTest$Walker2/walkAllStackframes,
StackWalkerTest$Walker2/Method2,
StackWalkerTest$Walker2/Method1,
StackWalkerTest$Walker2/walk,
StackWalkerTest$Walker1/walk,
StackWalkerTest/walk,
StackWalkerTest/main
]
--- Skip some StackFrames ---
[
StackWalkerTest$Walker2/walk,
StackWalkerTest$Walker1/walk,
StackWalkerTest/walk,
StackWalkerTest/main
]
--- Limit StackFrames ---
[
StackWalkerTest$Walker2/walkLimitStackframes,
StackWalkerTest$Walker2/Method2,
StackWalkerTest$Walker2/Method1
]

ポイント解説

上記のコードにおける重要なポイントは以下の通りです。

  • walkAllStackframes():ストリームのmap()で各フレームのクラス名とメソッド名を結合した文字列に変換し、collect()ですべてのフレームをリストとして収集しています。
  • walkSomeStackframes():中間操作のskip(3)により、先頭から3つのフレームをスキップした結果を出力しています。
  • walkLimitStackframes():中間操作のlimit(3)により、最初の3つのフレームのみを取得しています。
  • StackWalker.Option.RETAIN_CLASS_REFERENCE:各スタックフレームから呼び出し元クラスのClassオブジェクトへの参照を保持できるようにするオプションです。
  • StackWalker.Option.SHOW_HIDDEN_FRAMES:通常は非表示となる特殊なフレーム(ラムダ式など)も表示対象に含めるオプションです。
  1. Javaでスタックを使ってキュー(Queue)を実装する方法を解説

    キューとスタックの基本Queue(キュー)は Collection インターフェースを継承したクラスで、FIFO(First-In-First-Out:先入れ先出し)方式による要素の挿入と削除をサポートします。一方、Stack(スタック)は Vector クラスのサブクラスであり、LIFO(Last-In-First-Out:後入れ先出し)方式でオブジェクトを管理します。つまり、スタックの一番上に追加された最後の要素が、最初に取り出される要素になります。この2つのデータ構造の性質は正反対ですが、スタックを2つ組み合わせることで、キューを実装することが可能です。以下では、その具体的な実装方法を紹

  2. Javaでキュー(Queue)を使ってスタック(Stack)を実装する方法

    Stack(スタック)はVectorクラスのサブクラスであり、オブジェクトを後入れ先出し(LIFO:Last-In-First-Out)で管理するデータ構造です。スタックの最上部に最後に追加された要素が、最初に取り出される要素になります。 一方、Queue(キュー)はCollectionインターフェースを継承しており、先入れ先出し(FIFO:First-In-First-Out)に基づく挿入(insert)と削除(remove)の操作をサポートします。 一見すると性質の異なるこの2つのデータ構造ですが、キューの使い方を少し工夫するだけで、キューのみを使ってスタックを実装することができます。 キ