Tại sao sách .Net nói về stack vs phân bổ bộ nhớ heap?


36

Có vẻ như mọi cuốn sách .net đều nói về các loại giá trị so với các loại tham chiếu và làm cho nó trở thành một trạng thái (thường không chính xác) trong đó mỗi loại được lưu trữ - heap hoặc ngăn xếp. Thông thường nó ở một vài chương đầu tiên và được trình bày như một sự thật quan trọng. Tôi nghĩ rằng nó thậm chí được bao phủ trong các kỳ thi chứng chỉ . Tại sao stack vs heap thậm chí quan trọng đối với (người mới bắt đầu) .Net developers? Bạn phân bổ công cụ và nó chỉ hoạt động, phải không?


11
Một số tác giả chỉ có đánh giá thực sự tồi tệ về những gì quan trọng để dạy người mới bắt đầu và những gì là tiếng ồn không liên quan. Trong một cuốn sách tôi thấy gần đây, lần đầu tiên đề cập đến các sửa đổi truy cập đã bao gồm nội bộ được bảo vệ , điều mà tôi chưa bao giờ sử dụng trong 6 năm của C # ...
Timwi

1
Tôi đoán là bất cứ ai đã viết tài liệu .Net gốc cho phần đó đã tạo ra một phần lớn của nó, và tài liệu đó là những gì các tác giả ban đầu dựa trên sách của họ, và sau đó nó chỉ ở lại.
Greg

Nói rằng các loại giá trị sao chép toàn bộ nội dung xung quanh và các tham chiếu thì không, sẽ có ý nghĩa hơn và dễ hiểu hơn tại sao nên sử dụng các tham chiếu, vì nơi các giá trị đó được lưu trữ có thể được triển khai cụ thể và thậm chí không liên quan.
Trinidad

Phỏng vấn sùng bái hàng hóa?
Den

Câu trả lời:


37

Tôi đang trở nên tin rằng lý do chính khiến thông tin này được coi là quan trọng là truyền thống. Trong các môi trường không được quản lý, sự khác biệt giữa stack và heap rất quan trọng và chúng ta phải phân bổ và xóa thủ công bộ nhớ chúng ta sử dụng. Bây giờ, bộ sưu tập rác chăm sóc quản lý, vì vậy họ bỏ qua bit đó. Tôi không nghĩ rằng thông điệp đã thực sự thông qua rằng chúng ta không cần phải quan tâm loại bộ nhớ nào được sử dụng.

Như Fede đã chỉ ra, Eric Lippert có một số điều rất thú vị để nói về điều này: http://bloss.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx .

Trước thông tin đó, bạn có thể điều chỉnh đoạn đầu tiên của tôi để đọc về cơ bản: "Lý do mọi người bao gồm thông tin này và cho rằng nó quan trọng là vì thông tin không chính xác hoặc không đầy đủ kết hợp với việc cần kiến ​​thức này trong quá khứ."

Đối với những người cho rằng nó vẫn quan trọng vì lý do hiệu suất: Bạn sẽ thực hiện những hành động nào để chuyển thứ gì đó từ đống sang đống nếu bạn đo lường mọi thứ và phát hiện ra rằng nó có vấn đề? Nhiều khả năng, bạn sẽ tìm thấy một cách hoàn toàn khác để cải thiện hiệu suất cho khu vực có vấn đề.


6
Tôi đã nghe nói rằng trong một số triển khai khung (cụ thể là trên Xbox), tốt hơn là sử dụng các cấu trúc trong giai đoạn kết xuất (chính trò chơi) để cắt giảm bộ sưu tập rác. Bạn vẫn sẽ sử dụng các loại bình thường ở nơi khác, nhưng được phân bổ để GC không chạy trong trò chơi. Đó là về tối ưu hóa duy nhất liên quan đến stack vs heap mà tôi biết trong .NET và nó khá cụ thể đối với các nhu cầu của khung nhỏ gọn và các chương trình thời gian thực.
CodexArcanum

5
Tôi hầu hết đồng ý với lập luận của truyền thống. Nhiều lập trình viên có kinh nghiệm tại một số điểm có thể đã lập trình bằng ngôn ngữ cấp thấp nơi công cụ này quan trọng nếu bạn muốn mã chính xác và hiệu quả. Tuy nhiên, lấy C ++ làm ngôn ngữ không được quản lý: Đặc tả chính thức không thực sự nói rằng các biến tự động phải đi trên ngăn xếp, v.v. Tiêu chuẩn của C ++ coi stack và heap là chi tiết triển khai. +1
stakx

36

Dường như mọi cuốn sách .NET đều nói về các loại giá trị so với các loại tham chiếu và làm cho nó trở thành một trạng thái (thường không chính xác) trong đó mỗi loại được lưu trữ - heap hoặc ngăn xếp. Thông thường nó trong một vài chương đầu tiên và được trình bày như một sự thật quan trọng.

Tôi hoàn toàn đồng ý; Tôi thấy điều này tất cả các thời gian.

Tại sao sách .NET nói về stack vs heap memory memory?

Một phần lý do là vì nhiều người đã đến C # (hoặc các ngôn ngữ .NET khác) từ nền C hoặc C ++. Vì các ngôn ngữ đó không áp dụng cho bạn các quy tắc về thời gian lưu trữ, nên bạn phải biết các quy tắc đó và thực hiện chương trình của mình một cách cẩn thận để tuân theo chúng.

Bây giờ, biết các quy tắc đó và tuân theo chúng trong C không yêu cầu bạn phải hiểu "đống" và "ngăn xếp". Nhưng nếu bạn hiểu cách các cấu trúc dữ liệu hoạt động thì thường dễ hiểu và tuân theo các quy tắc hơn.

Khi viết một cuốn sách dành cho người mới bắt đầu, việc tác giả giải thích các khái niệm theo thứ tự mà họ đã học là điều đương nhiên. Đó không nhất thiết là thứ tự có ý nghĩa cho người dùng. Gần đây tôi là biên tập viên kỹ thuật cho cuốn sách dành cho người mới bắt đầu C # 4 của Scott Dorman, và một trong những điều tôi thích là Scott đã chọn một thứ tự khá hợp lý cho các chủ đề, thay vì bắt đầu từ những chủ đề thực sự tiên tiến trong quản lý bộ nhớ.

Một phần khác của lý do là một số trang trong tài liệu MSDN nhấn mạnh mạnh vào việc cân nhắc lưu trữ. Đặc biệt tài liệu MSDN cũ hơn vẫn còn lảng vảng từ những ngày đầu. Phần lớn tài liệu đó có những lỗi tinh vi chưa bao giờ được cắt bỏ, và bạn phải nhớ rằng nó được viết vào thời điểm cụ thể trong lịch sử và cho một đối tượng cụ thể.

Tại sao stack vs heap thậm chí quan trọng đối với các nhà phát triển .NET (người mới bắt đầu)?

Theo tôi, nó không. Điều quan trọng hơn nhiều để hiểu là những thứ như:

  • Sự khác biệt về ngữ nghĩa sao chép giữa loại tham chiếu và loại giá trị là gì?
  • Làm thế nào để một tham số "ref int x" hoạt động?
  • Tại sao các loại giá trị là bất biến?

Và như vậy.

Bạn phân bổ công cụ và nó chỉ hoạt động, phải không?

Đó là lý tưởng.

Bây giờ, có những tình huống trong đó nó có vấn đề. Bộ sưu tập rác là tuyệt vời và tương đối rẻ tiền, nhưng nó không miễn phí. Sao chép các cấu trúc nhỏ xung quanh là tương đối rẻ tiền, nhưng không miễn phí. Có những kịch bản hiệu suất thực tế trong đó bạn phải cân bằng chi phí của áp lực thu gom so với chi phí sao chép quá mức. Trong những trường hợp đó, rất hữu ích để có một sự hiểu biết mạnh mẽ về kích thước, vị trí và tuổi thọ thực tế của tất cả các bộ nhớ có liên quan.

Tương tự, có các kịch bản xen kẽ thực tế, trong đó cần phải biết những gì trên ngăn xếp và những gì trên đống, và những gì người thu gom rác có thể di chuyển xung quanh. Đó là lý do tại sao C # có các tính năng như "cố định", "stackalloc", v.v.

Nhưng đó là tất cả các kịch bản nâng cao. Lý tưởng nhất là một lập trình viên mới bắt đầu cần lo lắng về những thứ này.


2
Cảm ơn câu trả lời của Eric. Các bài đăng trên blog gần đây của bạn về chủ đề này là những gì thực sự thôi thúc tôi gửi câu hỏi.
Greg

13

Các bạn đang thiếu điểm. Lý do tại sao sự phân biệt stack / heap là quan trọng là vì phạm vi .

struct S { ... }

void f() {
    var x = new S();
    ...
 }

Khi x đi ra khỏi phạm vi, đối tượng được tạo sẽ biến mất hoàn toàn . Đó chỉ là vì nó được phân bổ trên stack chứ không phải heap. Không có gì có thể xảy ra trong phần "..." của phương pháp có thể thay đổi thực tế đó. Cụ thể, bất kỳ bài tập hoặc lệnh gọi phương thức nào cũng chỉ có thể tạo các bản sao của cấu trúc S, không tạo các tham chiếu mới cho nó để cho phép nó tiếp tục sống.

class C { ... }

void f() {
     var x = new C();
     ...
}

Câu chuyện hoàn toàn khác! Vì x hiện đang ở trên heap , nên đối tượng của nó (nghĩa là chính đối tượng đó , không phải là bản sao của nó) rất có thể tiếp tục tồn tại sau khi x đi ra khỏi phạm vi. Trên thực tế, cách duy nhất nó sẽ không tiếp tục sống là nếu x là tham chiếu duy nhất cho nó. Nếu các bài tập hoặc phương thức gọi trong phần "..." đã tạo các tham chiếu khác vẫn còn "sống" theo thời gian x vượt quá phạm vi, thì đối tượng đó sẽ tiếp tục sống.

Đó là một khái niệm rất quan trọng và cách duy nhất để thực sự hiểu "cái gì và tại sao" là biết sự khác biệt giữa phân bổ stack và heap.


Tôi không chắc chắn tôi đã thấy lập luận đó được trình bày trước đây trong sách cùng với thảo luận chồng / đống, nhưng đó là một cuộc tranh luận tốt. +1
Greg

2
Do cách C # tạo các bao đóng, mã trong ...có thể gây ra xđược chuyển đổi thành một trường của lớp do trình biên dịch tạo ra, và do đó tồn tại lâu hơn phạm vi được chỉ định. Cá nhân, tôi thấy khó chịu với ý tưởng về việc nâng hàng ngầm, nhưng các nhà thiết kế ngôn ngữ dường như thực hiện nó (trái ngược với việc yêu cầu bất kỳ biến nào được nâng lên đều có một cái gì đó trong tuyên bố của nó để xác định điều đó). Để đảm bảo tính chính xác của chương trình, thường cần phải tính đến tất cả các tham chiếu có thể tồn tại cho một đối tượng. Biết rằng vào thời điểm một thói quen trở lại, không có bản sao của tài liệu tham khảo được thông qua sẽ vẫn hữu ích.
supercat

1
Đối với 'cấu trúc nằm trên ngăn xếp', tuyên bố đúng là nếu một vị trí lưu trữ được khai báo là structType foo, vị trí lưu trữ foochứa nội dung của các trường của nó; nếu foolà trên ngăn xếp, thì các trường của nó cũng vậy. Nếu foolà trên đống, các lĩnh vực của nó cũng vậy. nếu footrong Apple II được nối mạng, thì các trường của nó cũng vậy. Ngược lại, nếu foolà một loại lớp, nó sẽ giữ nullhoặc tham chiếu đến một đối tượng. Tình huống duy nhất trong đó loại lớp foocó thể được nói giữ các trường của đối tượng sẽ là nếu đó là trường duy nhất của một lớp và giữ một tham chiếu đến chính nó.
supercat

+1, tôi yêu cái nhìn sâu sắc của bạn ở đây và tôi nghĩ nó hợp lệ ... Tuy nhiên, tôi không cảm thấy đó là lý do tại sao các cuốn sách đề cập đến chủ đề này ở độ sâu như vậy. Có vẻ như những gì bạn giải thích ở đây có thể thay thế 3 hoặc 4 chương của cuốn sách nói trên và trở nên hữu ích hơn.
Frank V

1
từ những gì tôi biết, các cấu trúc không phải, cũng không thực sự luôn đi theo chồng.
sara

5

Về lý do TẠI SAO họ đề cập đến chủ đề này, tôi đồng ý với @Kirk rằng đó là một khái niệm quan trọng mà bạn phải hiểu. Bạn càng biết rõ các cơ chế, bạn càng có thể làm tốt hơn để tạo ra các ứng dụng tuyệt vời hoạt động trơn tru.

Bây giờ Eric Lippert dường như đồng ý với bạn rằng chủ đề không được đề cập chính xác bởi hầu hết các tác giả. Tôi khuyên bạn nên đọc blog của anh ấy để đạt được sự hiểu biết tuyệt vời về những gì dưới mui xe.


2
Bài viết của Eric đưa ra quan điểm mà tất cả những gì bạn cần biết là các đặc điểm công khai của các loại giá trị và tham chiếu, và không nên mong đợi việc triển khai thậm chí giữ nguyên. Tôi nghĩ rằng đó là một câu hỏi khá hay để đề xuất rằng có một cách hiệu quả để thực hiện lại C # mà không cần ngăn xếp nhưng quan điểm của anh ấy là chính xác: đó không phải là một phần của thông số ngôn ngữ. Vì vậy, lý do duy nhất để sử dụng lời giải thích này mà tôi có thể nghĩ đến là vì nó là một câu chuyện ngụ ngôn hữu ích cho các lập trình viên biết các ngôn ngữ khác, đặc biệt là C. Miễn là họ biết đó là một câu chuyện ngụ ngôn, mà nhiều tài liệu không làm rõ.
Jeremy

5

Chà, tôi nghĩ đó là toàn bộ quan điểm của môi trường được quản lý. Tôi thậm chí còn đi xa hơn khi gọi đây là chi tiết triển khai của thời gian chạy cơ bản mà bạn KHÔNG nên đưa ra bất kỳ giả định nào, bởi vì nó có thể thay đổi bất cứ lúc nào.

Tôi không biết nhiều về .NET, nhưng theo như tôi biết, JITted của nó trước khi thực thi. Ví dụ, JIT có thể thực hiện phân tích thoát và những gì không và bất ngờ bạn sẽ có các đối tượng nằm trên ngăn xếp hoặc chỉ trong một số thanh ghi. Bạn không thể biết điều này.

Tôi cho rằng một số cuốn sách chỉ đơn giản là vì các tác giả gán tầm quan trọng lớn cho nó hoặc bởi vì họ cho rằng khán giả của họ làm điều đó (ví dụ nếu bạn viết "C # cho lập trình viên C ++" thì có lẽ bạn nên đề cập đến chủ đề này).

Tuy nhiên, tôi nghĩ không có nhiều điều để nói hơn là "bộ nhớ được quản lý". Nếu không mọi người có thể rút ra kết luận sai.


2

Bạn phải hiểu cách phân bổ bộ nhớ hoạt động để sử dụng nó hiệu quả ngay cả khi bạn không phải quản lý nó một cách rõ ràng. Điều này áp dụng cho khá nhiều sự trừu tượng trong khoa học máy tính.


2
Trong các ngôn ngữ được quản lý, bạn phải biết sự khác biệt giữa loại giá trị và loại tham chiếu, nhưng ngoài điều đó, thật dễ dàng để bị cuốn theo trục nghĩ về cách nó được quản lý dưới mui xe. Xem ở đây để biết ví dụ: stackoverflow.com/questions/4083981/ trộm
Robert Harvey

Tôi phải đồng ý với Robert

Sự khác biệt giữa heap được phân bổ và ngăn xếp được phân bổ chính xác là những gì giải thích sự khác biệt giữa các loại giá trị và tham chiếu.
Jeremy


1
Jeremy, sự khác biệt giữa phân bổ heap và stack không thể giải thích hành vi khác nhau giữa các loại giá trị và loại tham chiếu, bởi vì có những lúc cả hai loại giá trị và loại tham chiếu nằm trên heap, và chúng hành xử khác nhau. Điều quan trọng hơn để hiểu là (ví dụ) khi bạn cần sử dụng tham chiếu qua cho các loại tham chiếu so với các loại giá trị. Điều này chỉ phụ thuộc vào "nó là loại giá trị hay loại tham chiếu", chứ không phải "nó nằm trên heap".
Tim Goodman

2

Có thể có một số trường hợp cạnh mà nó có thể làm cho một sự khác biệt. Không gian ngăn xếp mặc định là 1meg trong khi heap là vài gig. Vì vậy, nếu giải pháp của bạn chứa một số lượng lớn đối tượng, bạn có thể hết dung lượng ngăn xếp trong khi có nhiều không gian heap.

Tuy nhiên, đối với hầu hết các phần nó là khá hàn lâm.


Có nhưng tôi nghi ngờ bất kỳ cuốn sách nào cũng phải chịu khó giải thích rằng chính các tài liệu tham khảo được lưu trữ trên ngăn xếp - vì vậy không có vấn đề gì nếu bạn có nhiều loại tham chiếu hoặc nhiều loại giá trị mà bạn vẫn có thể bị tràn ngăn xếp.
Jeremy

0

Như bạn nói, C # có nhiệm vụ trừu tượng hóa việc quản lý bộ nhớ và phân bổ heap so với ngăn xếp là các chi tiết triển khai mà theo lý thuyết mà nhà phát triển không cần phải biết.

Vấn đề là một số điều thực sự khó giải thích một cách trực quan mà không đề cập đến các chi tiết thực hiện này. Cố gắng giải thích hành vi có thể quan sát được khi bạn sửa đổi các loại giá trị có thể thay đổi - gần như không thể thực hiện được nếu không đề cập đến sự phân biệt stack / heap. Hoặc cố gắng giải thích tại sao thậm chí có loại giá trị trong ngôn ngữ ngay từ đầu và khi nào bạn sẽ sử dụng chúng? Bạn cần hiểu sự khác biệt để có ý nghĩa của ngôn ngữ.

Lưu ý rằng các cuốn sách nói về Python hoặc JavaScript không tạo ra vấn đề lớn nếu họ thậm chí đề cập đến nó. Điều này là do mọi thứ đều được phân bổ hoặc không thay đổi, có nghĩa là các ngữ nghĩa sao chép khác nhau không bao giờ được sử dụng. Trong các ngôn ngữ đó, sự trừu tượng của bộ nhớ hoạt động, trong C #, nó bị rò rỉ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.