2012-02-24 10 views
15

実行時に各メソッドが消費するスタックメモリの量を調べようとしています。タスクを実行するには、私はちょうどm()が呼び出された回数を私に言って整数を印刷StackOverflowErrorメソッドのスタックメモリをJavaで推測する

public class Main { 
    private static int i = 0; 

    public static void main(String[] args) { 
     try { 
      m(); 
     } catch (StackOverflowError e) { 
      System.err.println(i); 
     } 
    } 

    private static void m() { 
     ++i; 
     m(); 
    } 
} 

を強制します。この簡単なプログラムを考案しました。私は手動で次の値を取得し、さまざまな値(128K、256K、384K)にJVMのスタックサイズ(-Xss VMパラメータ)を設定しました:

stack i  delta 
    128  1102 
    256  2723 1621 
    384  4367 1644 

デルタは私が計算され、それが最後の値だましたラインの私と現在のもの。予想どおり、修正されています。そしてそこに問題がある。私が知っているように、スタックサイズのメモリの増分は128kであり、これは呼び出しごとに80byteのメモリ使用量(これは誇張されているようです)のようなものをもたらします。 BytecodeViewerの中m()を見上げる

は、我々は、これは静的メソッドで、そこにはthisパラメータ渡しはません、とm()は引数を持っていないということを知っている2のスタックの最大の深さを取得します。また、戻りアドレスポインタも考慮する必要があります。だから、メソッド呼び出しごとに3 * 8 = 24バイトのようなものがなければなりません(私は変数あたり8バイトと仮定していますが、もちろんそれは完全にオフかもしれません)。それより少し上回っていても、48バイトと言うと、まだ80バイトの値から離れています。

私はメモリアラインメントと関係があると思っていましたが、その場合、約64または128バイトの値を持つことになります。

私は64ビットWindows7 OSで64ビットJVMを実行しています。

私はいくつかの前提を定めていますが、その中にはいくつかの理由があります。その場合、私はすべての耳です。

誰もが、私はこのI must be frank..

答えて

2

この質問は、私の頭の上にあるかもしれませんが、おそらくもっと深いところで話しているかもしれませんが、とにかく私の答えをそこに投げ捨てます。

まず、return address pointerで何を参照していますか?メソッドが終了すると、スタックフレームから戻りメソッドがポップされます。したがって、実行メソッドのFrameには戻りアドレスは格納されません。

フレームにはローカル変数が格納されます。それは静的でパラメータがないので、あなたが言うようにこれらは空でなければならず、opスタックと地方のサイズはコンパイル時に固定され、各ユニットの幅は32ビットです。しかし、これと同様に、このメソッドは、それが属するクラスの定数プールへの参照をも持っていなければなりません。

さらに、JVM仕様では、メソッドフレームmay be extended with additional implementation-specific information, such as debugging information.が指定されています。これは、コンパイラによって残りのバイトを説明できます。

OpenJDKのソースを精練すべてJVM Specification on Frames.

から供給UPDATE

は、メソッド呼び出しのフレームに渡される構造体であるように思われ、これを明らかにする。内何を期待するのはかなり良い洞察力を与える:

/* Invoke types */ 

#define INVOKE_CONSTRUCTOR 1 
#define INVOKE_STATIC  2 
#define INVOKE_INSTANCE 3 

typedef struct InvokeRequest { 
    jboolean pending;  /* Is an invoke requested? */ 
    jboolean started;  /* Is an invoke happening? */ 
    jboolean available; /* Is the thread in an invokable state? */ 
    jboolean detached;  /* Has the requesting debugger detached? */ 
    jint id; 
    /* Input */ 
    jbyte invokeType; 
    jbyte options; 
    jclass clazz; 
    jmethodID method; 
    jobject instance; /* for INVOKE_INSTANCE only */ 
    jvalue *arguments; 
    jint argumentCount; 
    char *methodSignature; 
    /* Output */ 
    jvalue returnValue; /* if no exception, for all but INVOKE_CONSTRUCTOR */ 
    jobject exception; /* NULL if no exception was thrown */ 
} InvokeRequest; 

Source

+0

それはいくつかの洞察力のある情報でした。あなたはそれぞれのメソッド呼び出しが80バイトかかると思われる理由について理論化できますか? –

+0

自分のJVM実装がFrame構造内でどのような情報を保持しているかを知ることができますか? – Jivings

+0

@devouredelysium OpenJDKソースで私の答えを更新しました。 – Jivings

4

をやっている理由を尋ねる開始する前にあなたはスタックに命令ポインタ(8バイト)を含める必要があり、あなたが信じていない場合でも、保存されている他のコンテキスト情報があるかもしれませんそれが必要になるでしょう。アライメントは16バイト、ヒープのような8バイトです。例えば戻り値がない場合でも戻り値として8バイトを予約できます。

Javaは多くの言語のように再帰の重い使用に適していません。例えばこの場合、プログラムを永久に実行させるテールコール最適化は行いません。 ;)

+0

はい、私は明示的に24bytesの2つの変数プラスのリターンアドレスが含まれていることを述べることを忘れていました。 –

+3

"必要があると思わない場合でも保存される他のコンテキスト情報があるかもしれません。"これは私が知りたいことです!私はこの問題で光を放つために利用できる誰かにクッキーとアルコールを与えています! –

+1

JNI呼び出しでは、jenv(環境)とjclass(クラス)が含まれています。それを解決する最善の方法は、OpenJDKのコードを読むことです。 –

関連する問題