2010-04-01 35 views
13

Tôi thực sự sẽ đánh giá cao nếu ai đó có thể cho tôi biết liệu tôi hiểu nó tốt:Các tham chiếu của các đối tượng này trên Stack hay trên Heap?

class X 
{ 
    A a1=new A(); // reference on the stack, object value on the heap 
    a1.VarA=5; // on the stack - value type 
    A a2=a1;  // reference on the stack, object value on the heap 
    a2.VarA=10; // on the stack - value type   
} 

Ngoài ra cả hai tài liệu tham khảo a1a2 là trên stack, trong khi "đối tượng" giá trị của họ đang ở trên heap. Nhưng những gì về VarA biến, loại giá trị tinh khiết của nó?

class A 
{ 
    int VarA; 
} 
+3

Vì mã này không biên dịch thực sự khó khăn của nó để mô tả cách thức xử lý thời gian chạy nó. Tất cả những câu nói đó có ý định nằm bên trong một thân phương pháp? Là những khai báo trường hoặc khai báo biến cục bộ? –

Trả lời

27

Bạn đang đặt câu hỏi chi tiết thực hiện, vì vậy câu trả lời sẽ phụ thuộc vào việc thực hiện cụ thể. Hãy xem xét một phiên bản chương trình của bạn thực sự biên dịch:

class A { public int VarA; } 
class X 
{ 
    static void Main(string[] args) 
    { 
     A a1 = new A(); 
     a1.VarA = 5; 
     A a2 = a1; 
     a2.VarA = 10; 
    } 
} 

đây là những gì xảy ra trên CLR 4.0 của Microsoft, chạy C# 4.0, trong chế độ Gỡ lỗi.

Tại thời điểm này, con trỏ khung ngăn xếp đã được sao chép vào ebp đăng ký:

Ở đây chúng tôi cấp phát bộ nhớ heap cho đối tượng mới.

A a1 = new A(); 
mov   ecx,382518h 
call  FFE6FD30 

Điều đó trả về tham chiếu đến đối tượng đống trong eax. Chúng tôi lưu trữ các tham chiếu trong khe ngăn xếp ebp-48, đó là một khe tạm thời không liên kết với bất kỳ tên nào. Hãy nhớ rằng, a1 vẫn chưa được khởi tạo.

mov   dword ptr [ebp-48h],eax 

Bây giờ chúng ta tham khảo, chúng tôi chỉ được lưu trữ trên stack và sao chép nó vào ecx, mà sẽ được sử dụng cho việc "này" con trỏ đến các cuộc gọi đến ctor.

mov   ecx,dword ptr [ebp-48h] 

Bây giờ chúng tôi gọi cho người quản lý.

call  FFE8A518 

Bây giờ chúng tôi sao chép tham chiếu được lưu trữ trong ngăn xếp tạm thời vào đăng ký eax một lần nữa.

mov   eax,dword ptr [ebp-48h] 

Và bây giờ chúng tôi sao chép tham chiếu trong eax vào khe ngăn xếp ebp-40, là a1.

mov   dword ptr [ebp-40h],eax 

Bây giờ chúng ta phải lấy a1 vào eax:

a1.VarA = 5; 
mov   eax,dword ptr [ebp-40h] 

Hãy nhớ rằng, eax bây giờ là địa chỉ của dữ liệu đống phân bổ cho điều được tham chiếu bởi a1. Các lĩnh vực Vara của điều đó là bốn byte vào đối tượng, vì vậy chúng tôi lưu trữ 5 thành rằng:

mov   dword ptr [eax+4],5 

Bây giờ chúng ta tạo một bản sao của tài liệu tham khảo trong khe ngăn xếp cho a1 vào eax, và sau đó sao chép đó vào khe ngăn xếp cho a2, là ebp-44.

A a2 = a1; 
mov   eax,dword ptr [ebp-40h] 
mov   dword ptr [ebp-44h],eax 

Và bây giờ như bạn mong muốn một lần nữa chúng ta được a2 vào eax và sau đó tôn kính các tài liệu tham khảo bốn byte để viết 0x0A vào Vara:

a2.VarA = 10; 
mov   eax,dword ptr [ebp-44h] 
mov   dword ptr [eax+4],0Ah 

Vì vậy, câu trả lời cho câu hỏi của bạn là tham chiếu đến đối tượng được lưu trữ trong ngăn xếp ở ba nơi: ebp-44, ebp-48 và ebp-40. Chúng được lưu trữ trong thanh ghi trong eax và ecx. Bộ nhớ của đối tượng, bao gồm cả trường của nó, được lưu trữ trên vùng quản lý. Đây là tất cả trên x86 trong xây dựng gỡ lỗi, của CLR v4.0 của Microsoft. Nếu bạn muốn biết làm thế nào công cụ được lưu trữ trên ngăn xếp, đống và đăng ký trong một số cấu hình khác, nó có thể hoàn toàn khác nhau. Tất cả các tài liệu tham khảo có thể được lưu trữ trên heap, hoặc tất cả trong sổ đăng ký; có thể không có ngăn xếp nào cả. Nó hoàn toàn phụ thuộc vào cách các tác giả của trình biên dịch jit quyết định triển khai ngữ nghĩa IL.

+0

Nó cũng phụ thuộc vào cách các tác giả của trình biên dịch C# quyết định triển khai ngữ nghĩa C#.Các biến cục bộ ('a1' và' a2') có thể được thực hiện dưới dạng các trường trong một loại được quản lý, chỉ để lại một tham chiếu trong mỗi khung ngăn xếp. Tôi nhận thấy việc đưa điều này lên trong một bình luận cho bài viết của bạn gợi lên những suy nghĩ của bà ngoại và sự hấp thu của trứng, nhưng tôi nghĩ tôi sẽ đề cập đến nó anyway :) –

+0

@Jon: Thật vậy. Có rất ít lỗi mà chúng tôi sản xuất trong giai đoạn tạo IL của trình biên dịch; một trong số họ là "quá nhiều người dân địa phương" - Tôi không nhớ giới hạn là gì nhưng nó giống như bạn không thể có hơn 32K hoặc 64K người dân địa phương hoặc thời gian trong một phương pháp. (Rõ ràng là mã thực không có vấn đề này nhưng mã máy được tạo ra có thể.) Tôi thường nghĩ rằng trong những trường hợp như vậy, chúng ta nên thay vì tạo ra lỗi, chỉ cần bắt đầu đưa chúng vào các trường. Nhưng nó quá mơ hồ một kịch bản để biện minh cho chi phí viết và kiểm tra mã. –

10

Nói đúng, phụ thuộc vào việc triển khai thực hiện. Thông thường, một nhà phát triển .NET không nên quan tâm đến những thứ này. Theo như tôi biết, trong việc thực thi .NET của Microsoft, các biến kiểu giá trị được lưu trữ trên stack (khi chúng được khai báo trong một phương thức), và dữ liệu của các đối tượng kiểu tham chiếu được cấp phát trên một vùng quản lý. Nhưng, hãy nhớ, khi một loại giá trị là một trường của một lớp, bản thân lớp dữ liệu được lưu trữ trên một đống (bao gồm tất cả các trường kiểu giá trị). Do đó, không trộn lẫn ngữ nghĩa (loại giá trị so với loại tham chiếu) với quy tắc phân bổ. Điều này có thể có hoặc không có thể tương quan.

2

Tôi nghĩ rằng bạn có thể có có một sự hiểu lầm nhỏ ...

Nói chung, các loại tài liệu tham khảo đi trên heap, và các loại giá trị/người dân địa phương Tôi tin (có thể sai) đi trên stack. Tuy nhiên, ví dụ A1.VarA và A2.VarA của bạn đề cập đến trường của loại tham chiếu - được lưu trữ cùng với đối tượng trên heap ...

+0

Có, nhưng giá trị của trường đó là int, do đó, loại giá trị, phải không? – Petr

+0

@Petr, tất cả các trường được chứa trong loại tham chiếu A, nằm trên vùng heap. –

2

Trong trường hợp này a1.VarA sẽ nằm trên vùng heap không gian cho nó sẽ được phân bổ khi bạn đã làm A a1 = new A().

Nếu bạn chỉ làm int i = 5; trong một chức năng mà sẽ đi trên stack nhưng khi bạn a1 quy định rõ ràng đã được cấp phát trên heap thì tất cả các loại giá trị liên kết với nó sẽ được đặt trên đống

0

đọc Jeff Richter's CLR via C# để hiểu rõ hơn về chủ đề này.

0

Hãy nhớ đọc trong C# ở Độ sâu: - Chỉ các biến cục bộ (biến được khai báo bên trong phương thức) và tham số phương thức trực tiếp trong ngăn xếp. Biến số như varA trong trường hợp trên nằm trên vùng heap.

+3

Lưu ý rằng các biến địa phương được đóng trên địa phương của một lambda hoặc phương pháp vô danh không được lưu trữ trên ngăn xếp trong việc thực hiện Microsoft của C#. Điều tương tự cũng xảy ra với các biến cục bộ nằm trong khối lặp. –

2
class X 
{ 
    A a1=new A(); // reference on the stack, object value on the heap 
    a1.VarA=5; // on the Heap- value type (Since it is inside a reference type) 
    A a2=a1;  // reference on the stack, object value on the heap 
    a2.VarA=10; // on the Heap - value type (Since it is inside a reference type) 
} 
0

Tôi cũng mới sử dụng C#. Câu hỏi của bạn rất quan trọng, tôi cũng nghĩ về nó. Tất cả các tài liệu cho biết, giá trị đi stack và tài liệu tham khảo đi đống, nhưng như những kẻ ở trên cho biết, nó chỉ cho các mã bên trong phương pháp. Trên cầu thang của việc học tôi nhận ra rằng tất cả các chương trình mã bắt đầu bên trong của một phương pháp thuộc về một thể hiện thuộc về đống. Vì vậy, khái niệm, ngăn xếp là không bằng nhau trong thuật ngữ với đống như tất cả các tài liệu gây nhầm lẫn cho mọi người. Các mecanism stack được tìm thấy chỉ trong một phương pháp ...

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