kagamihogeの日記

kagamihogeの日記です。

JEP 259: Stack-Walking APIをテキトーに訳した

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::getStackTraceThread::getStackTraceStackTraceElementオブジェクトの配列を返し、このオブジェクトにはスタックトレースの各要素のクラス名とメソッド名が含まれます。
  • protectedメソッドSecurityManager::getClassContextは、クラスコンテキストにアクセスするためのSecurityManagerサブクラスを許可します。 これらのAPIVMに対し、スタック全体のスナップショットをイーガーにキャプチャするよう要求し、スタック全体を表現する情報を返します。もし呼び出し元がスタックの上から数フレームにだけ関心があるとしても、全フレームを走査するコストを避ける方法はありません。Throwable::getStackTraceThread::getStackTraceの両メソッドともStackTraceElementオブジェクトの配列を返し、このオブジェクトにはクラス名とメソッド名は含まれますが、実際のClassインスタンスは含まれません。スタック全体に関心のあるアプリケーションの場合、VM実装でパフォーマンスのためにスタックの数フレームを削除することを仕様で許可しています。言い換えると、Thread::getStackTraceは部分的なスタックトレースを返す可能性があります。

これらのAPIは、JDK内部のsun.reflect.Reflection::getCallerClassメソッドに依存している、もしくは、パフォーマンスオーバーヘッドを許容出来ないユースケースを満たしていません。ユースケースには以下を含みます。

  • 直前の呼び出し元クラス(immediate caller's class)が見つかるまでスタックをウォークする。いずれのJDKの呼び出し元にセンシティブなAPIは、APIの振る舞いを決定するのに直前の呼び出し元クラスを調査します。たとえば、Class::forNameResourceBundle::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 predicate)は、呼び出し元フレームの参照を、フィルタリング有・無どちらでも行うためのメソッドです。

*1:Native JVM transitions will be minimizedが原文。どゆ意味?