http://openjdk.java.net/jeps/259 をテキトーに訳した。
JEP 259: Stack-Walking API
Owner Mandy Chung Created 2014/05/22 22:05 Updated 2015/08/03 19:26 Type Feature Status Candidate Component core-libs Scope JDK Discussion core dash libs dash dev at openjdk dot java dot net Effort M Duration M Priority 3 Reviewed by Brian Goetz, Mark Reinhold Release 9 Issue 8043814
Summary
スタックトレースの情報に、遅延アクセスとフィルタリングが容易な、標準stack walking APIを定義します。
Motivation
実行中のスタック上の指定フレームをトラバースし、各フレームのClass
インスタンスにアクセスする標準APIは存在しません。
スタックトレースへのアクセスを提供する既存APIは存在します。
Throwable::getStackTrace
とThread::getStackTrace
はStackTraceElement
オブジェクトの配列を返し、このオブジェクトにはスタックトレースの各要素のクラス名とメソッド名が含まれます。- protectedメソッドの
SecurityManager::getClassContext
は、クラスコンテキストにアクセスするためのSecurityManager
サブクラスを許可します。 これらのAPIはVMに対し、スタック全体のスナップショットをイーガーにキャプチャするよう要求し、スタック全体を表現する情報を返します。もし呼び出し元がスタックの上から数フレームにだけ関心があるとしても、全フレームを走査するコストを避ける方法はありません。Throwable::getStackTrace
とThread::getStackTrace
の両メソッドともStackTraceElement
オブジェクトの配列を返し、このオブジェクトにはクラス名とメソッド名は含まれますが、実際のClass
インスタンスは含まれません。スタック全体に関心のあるアプリケーションの場合、VM実装でパフォーマンスのためにスタックの数フレームを削除することを仕様で許可しています。言い換えると、Thread::getStackTrace
は部分的なスタックトレースを返す可能性があります。
これらのAPIは、JDK内部のsun.reflect.Reflection::getCallerClass
メソッドに依存している、もしくは、パフォーマンスオーバーヘッドを許容出来ないユースケースを満たしていません。ユースケースには以下を含みます。
- 直前の呼び出し元クラス(immediate caller's class)が見つかるまでスタックをウォークする。いずれのJDKの呼び出し元にセンシティブなAPIは、APIの振る舞いを決定するのに直前の呼び出し元クラスを調査します。たとえば、
Class::forName
とResourceBundle::getBundle
メソッドは、それぞれ、クラスとリソースバンドルのロードに、直前の呼び出し元クラスを使います。Class::getMethod
などのリフレクションAPIは、実行するセキュリティチェックを決定するために、直前の呼び出し元クラスを使います。 - 非フィルタリング対象フレームを見つけるために指定の実装のスタックフレームをフィルタリングしてスタックをウォークする。
java.util.logging
API, Log4j, Groovyランタイムは呼び出し元クラスを見つけるのに中間スタックフレーム(実装固有やリフレクションのフレームなど)をフィルタリングします。 - 最初の特権フレームに達するまでの全保護ドメインを見つけるためにスタックをウォークする。この動作はパーミッションチェックで必要とされます。
- スタック全体を深さ制限に達するまでウォークする。この動作は、
Throwable
オブジェクトのスタックトレース生成や、Thread::dumpStack
メソッドの実装で必要とされます。
Description
このJEPではstack-walking APIの定義を行います。このAPIは、遅延およびフレームフィルタリングが可能で、指定条件にマッチするフレームでストップするshort walksのサポートおよび、スタック全体をトラバースするlong walkをサポートします。
JVMには機能拡張が行われます。機能拡張により、柔軟なトラバースのメカニズム、要求したスタックフレーム情報の実体化、要求時に追加されたスタックフレームへの効果的な遅延アクセス、を可能にします。ネイティブJVMへの移行は最小化されます*1。The implementation will need to have a stable view of a thread's stack: Returning a stream holding a stack pointer for further manipulation in an uncontrolled manner will not work since, as soon as the stream factory returns, the JVM will be free to reorganize the control stack (via deoptimization, for example).このことはAPI定義にも影響を与えます。
スタックフレームのClass
オブジェクトへのアクセスがセキュリティを侵害しないよう、このAPIはセキュリティマネージャと共に動作している場合の振る舞いも定義します。
API
取り得るアプローチの一つとしては、スタックをトラバースするためのcapability-based StackWalker
APIを定義します。セキュリティパーミッションチェックはStackWalker
オブジェクトそれぞれに対して実行されます。チェックはオブジェクトの使用時ではなくコンストラクト時に行われます。以下のようなメソッドを定義します。
public <T> T walk(Function<Stream<StackFrame>, T> function); public Class<?> getCallerClass(); public Optional<StackFrame> findCaller(Predicate<StackFrame> predicate);
walk
メソッドはスタックフレームのストリームをトラバースする関数を引数に取ります。別の案としては、Stream
を返すmethod
を持つことですが、実現可能性と安全性を決定するのにさらなる調査と試作を必要とします。
getCallerClass() findCaller(Predicate
は、呼び出し元フレームの参照を、フィルタリング有・無どちらでも行うためのメソッドです。