2012-02-24 19 views
15

Tôi đang cố gắng xác định dung lượng bộ nhớ ngăn xếp mà mỗi phương thức tiêu thụ khi chạy. Để làm nhiệm vụ, tôi đã nghĩ ra chương trình đơn giản này sẽ chỉ buộc một StackOverflowError,Suy ra sử dụng bộ nhớ ngăn xếp của phương thức trong 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(); 
    } 
} 

in một số nguyên nói với tôi bao nhiêu lần m() được gọi. Tôi đã tự đặt kích thước ngăn xếp của JVM (-Xss tham số VM) với các giá trị khác nhau (128k, 256k, 384k), thu thập các giá trị sau:

stack i  delta 
    128  1102 
    256  2723 1621 
    384  4367 1644 

đồng bằng đã được tính toán của tôi, và đó là giá trị giữa người cuối cùng dòng của tôi và của người hiện tại. Đúng như dự đoán, nó đã được sửa. Và có nằm trong vấn đề. Như tôi biết sự gia tăng bộ nhớ kích thước ngăn xếp là 128k, mang lại một cái gì đó giống như một bộ nhớ 80byte sử dụng cho mỗi cuộc gọi (mà có vẻ phóng đại).

Nhìn lên m() trong BytecodeViewer, chúng tôi có được độ sâu tối đa của chồng 2. Chúng tôi biết đây là một phương pháp tĩnh và rằng không có this tham số đi qua, và rằng m() không có đối số. Chúng ta cũng phải xem xét đến con trỏ địa chỉ trả về. Vì vậy, nên có một cái gì đó như 3 * 8 = 24 byte được sử dụng cho mỗi cuộc gọi phương pháp (tôi giả định 8 byte cho mỗi biến, mà tất nhiên có thể được hoàn toàn tắt. Có phải không?). Thậm chí nếu nó nhiều hơn một chút, hãy nói 48byte, chúng tôi vẫn còn cách xa giá trị 80bytes.

Tôi nghĩ rằng nó có thể có liên quan đến bộ nhớ, nhưng sự thật là trong trường hợp đó chúng tôi sẽ có giá trị khoảng 64 hoặc 128 byte, tôi muốn nói.

Tôi đang chạy một JVM 64 bit theo Hệ điều hành Windows7 64 bit.

Tôi đã đưa ra một số giả định, một số giả định có thể hoàn toàn tắt. Là trường hợp đó, tôi là tất cả tai.

Trước khi bất cứ ai bắt đầu hỏi tại sao tôi đang làm điều này I must be frank..

Trả lời

2

Câu hỏi này có thể là cách trên đầu của tôi, có lẽ bạn đang nói về điều này ở một mức độ sâu hơn, nhưng tôi sẽ ném câu trả lời của tôi ra khỏi đó anyway.

Thứ nhất, bạn đề cập đến điều gì bởi return address pointer? Khi một phương thức kết thúc, phương thức trả về sẽ xuất hiện từ khung ngăn xếp. Vì vậy, không có địa chỉ trả về nào được lưu trữ trong khung phương thức thi hành.

Phương thức Khung lưu trữ biến cục bộ. Vì nó là tĩnh, và không có tham số, chúng nên trống rỗng như bạn nói, và kích thước của ngăn xếp và người dân địa phương được cố định tại thời gian biên dịch, với mỗi đơn vị trong mỗi 32bits rộng. Nhưng cũng như phương pháp này cũng phải có một tham chiếu đến nhóm hằng số của lớp mà nó thuộc về.

Trong bổ sung, đặc tả JVM chỉ định khung phương thức may be extended with additional implementation-specific information, such as debugging information. Điều này có thể giải thích các byte còn lại, tùy thuộc vào trình biên dịch.

Tất cả bắt nguồn từ JVM Specification on Frames.

CẬP NHẬT

cọ rửa nguồn OpenJDK tiết lộ này, mà dường như là struct được truyền cho Frames trên phương pháp gọi. Đưa ra một cái nhìn sâu sắc khá tốt về những gì mong đợi trong vòng:

/* 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

Đó là một số thông tin sâu sắc, thưa ngài. Mặc dù vậy, bạn có thể đưa ra giả thuyết về lý do tại sao mỗi cuộc gọi phương thức dường như mất 80 byte? –

+0

Tôi có thể cho bạn biết thông tin nào về việc triển khai JVM của riêng tôi nắm giữ trong cấu trúc Khung? – Jivings

+0

@devouredelysium Cập nhật câu trả lời của tôi với nguồn OpenJDK. – Jivings

4

Bạn cần phải bao gồm trong ngăn xếp con trỏ hướng dẫn (8 byte) và có thể có thông tin bối cảnh khác mà sẽ được lưu ngay cả khi bạn không tin nó sẽ cần phải được. Sự liên kết có thể là 16 byte, 8 byte như đống. ví dụ. nó có thể dự trữ 8 byte cho giá trị trả về ngay cả khi không có.

Java không phù hợp với việc sử dụng nhiều đệ quy như nhiều ngôn ngữ. ví dụ. nó không làm tối ưu hóa cuộc gọi đuôi mà trong trường hợp này sẽ làm cho chương trình của bạn chạy mãi mãi. ;)

+0

Vâng, tôi quên nói rõ rằng 24bytes bao gồm 2 biến cộng với địa chỉ trả lại. –

+3

"có thể có các thông tin ngữ cảnh khác được lưu ngay cả khi bạn không tin rằng nó sẽ cần." Đó là những gì tôi muốn biết! Tôi đang cung cấp cookie và rượu cho bất kỳ ai có sẵn để làm sáng tỏ một số vấn đề! –

+1

Trong các cuộc gọi JNI, jenv (môi trường) và jclass (lớp) được bao gồm. Cách tốt nhất để làm việc đó là đọc mã OpenJDK. –

Các vấn đề liên quan