2012-02-24 54 views
15

我想確定每個方法在運行時消耗多少堆棧內存。做任務,我已經設計了這個簡單的程序,只會迫使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(); 
    } 
} 

打印整數告訴我m()了多少次調用。我手動設置JVM的堆棧大小(-Xss VM參數)不同的值(128K,256K,384K),獲得以下值:

stack i  delta 
    128  1102 
    256  2723 1621 
    384  4367 1644 

三角洲是由我計算,它的最後的值線我和現在的。如預期的那樣,它是固定的這就是問題所在。據我所知,棧大小的內存增量是128k,這可以產生類似於每個調用80byte的內存使用情況(這看起來很誇張)。

在BytecodeViewer中查找m(),我們得到一個堆棧的最大深度爲2.我們知道這是一個靜態方法,並且沒有this參數傳遞,並且m()沒有參數。我們還必須考慮返回地址指針。所以應該有類似於每個方法調用的3 * 8 = 24個字節(我假設每個變量8個字節,這當然可能完全關閉,是嗎?)。即使比這還要多一點,比如說48bytes,我們仍然遠離80bytes的值。

我認爲這可能與內存對齊有關,但事實是,在這種情況下,我們會有大約64或128字節的值,我會說。

我在64位Windows7操作系統下運行64位JVM。

我做了幾個假設,其中一些可能完全關閉。既然如此,我就是耳朵。

在任何人開始問我爲什麼做這個I must be frank..

回答

2

這個問題可能會超出我的頭,也許你正在更深層次地討論這個問題,但我仍然會把我的答案拋出去。

首先,你指的是return address pointer?當一個方法完成時,返回方法從堆棧框架彈出。所以沒有返回地址存儲在正在執行的方法框架內。

方法Frame存儲局部變量。由於它是靜態的和無參數的,因此它們應該是空的,並且在編譯時固定操作堆棧和本地的大小,每個單元的寬度都是32位。但是,除此之外,該方法還必須提及其所屬類別的常量池。

另外,JVM規範指定方法框架may be extended with additional implementation-specific information, such as debugging information.這可以解釋剩餘字節,具體取決於編譯器。

JVM Specification on Frames.

UPDATE

精練OpenJDK的源的所有來源的揭示本,這似乎是被傳遞到上方法調用的幀的結構。給出了什麼期望中一個很好的啓示:

/* 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個字節? – 2012-02-26 00:53:31

+0

我可以告訴你我自己的JVM實現在Frame結構中保存了什麼信息? – Jivings 2012-02-26 01:19:04

+0

@devouredelysium用OpenJDK來源更新了我的答案。 – Jivings 2012-02-26 01:35:43

4

您需要在堆棧包括指令指針(8個字節),並有可能即使你不相信這是保存其它上下文信息它需要是的。對齊可以是16個字節,像堆一樣是8個字節。例如即使沒有一個,它也可以爲返回值保留8個字節。

Java不適合像很多語言那樣大量使用遞歸。例如它不會執行尾部優化,在這種情況下會導致程序永遠運行。 ;)

+0

是的,我忘了明確規定了24bytes包括2個變量加上返回地址。 – 2012-02-24 14:44:41

+3

「即使您不相信它也需要保存,也可能會保存其他上下文信息。」這就是我想知道的!我正在給任何人提供餅乾和酒,以解決問題中的一些亮點! – 2012-02-24 14:46:02

+1

在JNI調用中,包含了jenv(環境)和jclass(類)。解決這個問題的最好方法是閱讀OpenJDK代碼。 – 2012-02-24 14:55:49