2011-11-17 607 views
25

thể trùng lặp:
What and where are the stack and heapTại sao bộ nhớ được chia thành đống và đống?

Tôi có một vài câu hỏi trên stack so với đống.

Điều cơ bản cần biết là ngăn xếp nhanh hơn đống, nhưng bị giới hạn. (đúng nếu tôi sai).

Tuy nhiên, tôi luôn tự hỏi cách stack và heap hoạt động chính xác. RAM chỉ là một phần của bộ nhớ, nó không được chia thành 'stack' và 'heap' (hay là nó?). Nếu vậy, tại sao chúng ta chia bộ nhớ trong ngăn xếp và đống ở vị trí đầu tiên?

Hệ điều hành có thể cho phép chúng tôi có thể phân bổ mọi thứ trên ngăn xếp -> mọi thứ diễn ra nhanh hơn -> thế giới hạnh phúc?

Tôi khá chắc chắn đó không phải là trường hợp. Nhưng tại sao!? Bất cứ ai có thể cho tôi một câu trả lời chuyên sâu?

Xin lỗi nếu bài đăng này trùng lặp với một số bài đăng của một người nào đó, có rất nhiều liên quan đến chồng và đống, tôi không thể tìm ra câu hỏi chính xác mà tôi có. Nếu bạn tình cờ biết một, hãy tiếp tục và liên kết nó.

+3

http://stackoverflow.com/questions/7123936/why-is-there-a-stack-and-a-heap – drdwilcox

+2

http://stackoverflow.com/questions/79923/what-and-where-are -the-stack-and-heap –

Trả lời

6

Bạn không thể chỉ sử dụng ngăn xếp, vì ngăn xếp yêu cầu phân bổ lần lượt trước hết & thứ tự sắp xếp (nghĩa là bạn chỉ có thể phân bổ dữ liệu được phân bổ mới nhất; cái mới hơn).

Thực ra, bạn có thể loại bỏ ngăn xếp (chỉ giữ vùng heap). Xem giấy của Appel Garbage Collection Can Be Faster Than Stack Allocation và sách Compiling with Continuation của Appel.

Và heap không có ý nghĩa xác định rõ ràng (trừ "bộ nhớ được cấp phát động không nằm trong ngăn xếp"). Trên thực tế, trên các hệ thống Linux, phân bổ một phần lớn bộ nhớ bằng cách sử dụng cuộc gọi hệ thống mmap là khá nhanh (nhưng malloc triển khai cố gắng tránh mmap và thích sử dụng lại free -d bộ nhớ). Vấn đề là phân bổ các vùng bộ nhớ nhỏ.

Và đọc thêm về garbage collection techniques. Trong C hoặc C++, bạn có thể sử dụng Boehm's GC

Ngăn xếp thường hữu ích, đặc biệt là cho các cuộc gọi hàm đệ quy. Nó rất hữu ích (ví dụ trong C) mà các bộ xử lý ngày nay thường có một thanh ghi ngăn xếp ngăn xếp chuyên dụng (được sử dụng bởi CALL & Hướng dẫn máy RET để gọi & trở về). Nhưng đây không phải lúc nào cũng vậy; trên một số bộ vi xử lý (ví dụ IBM360), con trỏ ngăn xếp là một thanh ghi thông thường, không phải là một bộ mã hóa cứng.

+0

thông tin rất hữu ích, cảm ơn :) – xcrypt

0

Bộ nhớ chỉ giống nhau cho cả hai, nhưng ngăn xếp và heap là 2 cấu trúc dữ liệu khác nhau hữu ích cho các mục đích khác nhau.

Ngăn xếp là một trừu tượng rất nguyên thủy cần thiết cho bất kỳ bộ vi xử lý vi mô nào để thực hiện các lệnh trên một vài toán hạng (thường là sổ đăng ký bộ xử lý hoặc địa chỉ bộ nhớ).

Vùng heap là vùng bộ nhớ phân bổ chung, nơi thường bạn muốn lưu trữ dữ liệu không bị ràng buộc vào ngăn xếp, đó là thời gian tồn tại lâu hơn nếu được lưu trữ trong ngăn xếp hoặc nói cách khác, dữ liệu sẽ được truy cập bằng các phần mã khác nhau.

+0

Vâng, tôi chỉ có thể phân bổ một số đối tượng trên ngăn xếp trong chức năng chính, và sử dụng nó trong suốt toàn bộ chương trình, tôi không cần đống cho điều đó. Lập luận của bạn có thể là ngăn xếp bị hạn chế, nhưng một trong những điều tôi định hỏi với câu hỏi của tôi là: Tại sao ngăn xếp bị giới hạn? (vì lý do nêu trên) – xcrypt

+0

Ngăn xếp bị giới hạn bởi vì nó phát triển từ một cực của địa chỉ bộ nhớ đến cực khác. Nếu nó không giới hạn, thì bạn có thể bị hỏng ngày lưu trữ trong heap khi bị gián đoạn (vì gián đoạn lưu trạng thái CPU trong ngăn xếp), vì vậy giới hạn nhân tạo phải được đặt ở đâu đó để tránh điều này (và đó là vì tồn tại thông báo 'tràn ngăn xếp' nổi tiếng khi bạn đạt đến giới hạn đó). –

23

Ngăn xếp: Ngăn xếp được sử dụng như một ngăn tạm thời để sử dụng bởi khối mã hiện đang thực thi và bất kỳ khối nào được gọi là mã hiện tại và bất kỳ khối nào được gọi là khối đó, v.v. Khi khối hiện tại thoát, các biến cục bộ mà nó đang sử dụng bị lãng quên. Như tên cho biết, ngăn xếp được sử dụng theo cách cuối cùng, đầu tiên.

Một trong những cách sử dụng quan trọng nhất của ngăn xếp là theo dõi chuỗi cuộc gọi hiện tại. Khi một hàm gọi một hàm khác, người gọi sẽ đẩy địa chỉ của lệnh tiếp theo (địa chỉ trả về) vào ngăn xếp. Khi mỗi hàm thoát, nó bật địa chỉ trả về của người gọi ra khỏi ngăn xếp và tiếp tục thực thi mã bắt đầu từ địa chỉ đó. Nó cũng được sử dụng để truyền thông số hàm và trả về giá trị giữa người gọi và callee.

Heap: Heap là khác nhau - không có thứ tự cụ thể cho nó. Nếu bạn muốn cấp phát bộ nhớ trong một khối mã và có thẻ nhớ đó nằm ngoài phần cuối của khối, bạn phân bổ bộ nhớ đó trên heap. Tất nhiên, bạn cũng sẽ cần phải lưu trữ một con trỏ/tham chiếu đến nó ở đâu đó để mã khác có thể tìm thấy bộ nhớ đó; hầu hết các ngôn ngữ cung cấp chỗ ở đó.

Tốc độ: Sự khác biệt về tốc độ không phải do bất kỳ thuộc tính nào của bộ nhớ - như bạn nói trong câu hỏi, cả chồng và đống thường nằm trong cùng một bộ nhớ vật lý. Phân bổ không gian trên ngăn xếp là nhanh chóng do các ngăn xếp LIFO bản chất: nếu bạn đẩy một cái gì đó vào ngăn xếp, chỉ có một nơi nó có thể kết thúc. Ngược lại, việc phân bổ một khối trên heap đòi hỏi phải tìm một khu vực tự do tiếp giáp đủ lớn trong bộ nhớ. Sự phân bổ stack có thể nhanh như một lệnh đơn; phân bổ đống yêu cầu cuộc gọi đến chức năng cấp phát bộ nhớ như malloc().

Tĩnh động: Phân bổ bộ nhớ trên heap là động - cho dù phân bổ một khối và kích thước của khối có thể được xác định theo đầu vào chương trình nhận được trong khi nó đang chạy. Các vùng bộ nhớ được phân bổ trên heap thậm chí có thể được thay đổi kích thước nếu cần thiết. Có thể cũng có thể để cấp phát bộ nhớ động trên ngăn xếp (xem hàm thư viện chuẩn C alloca()), nhưng bộ nhớ đó sẽ bị mất ngay khi chức năng hiện tại thoát. Stack phân bổ thường là tĩnh - trình biên dịch xác định bao nhiêu không gian là cần thiết cho các tham số (không đăng ký), trả về dữ liệu, và biến cục bộ, và nó tạo mã để dự trữ không gian cần thiết trên stack khi hàm được gọi.

Ví dụ: Hãy tưởng tượng bạn đang tạo bộ xử lý văn bản. Bạn không thể biết trước tài liệu sẽ lớn như thế nào, hoặc thậm chí bao nhiêu tài liệu sẽ được sử dụng cùng một lúc. Đồng thời, bạn muốn tài liệu của người dùng vẫn còn trong bộ nhớ miễn là người dùng muốn giữ chúng ở trạng thái mở. Nếu bạn cố gắng cấp phát bộ nhớ cho các tài liệu trên ngăn xếp, bạn sẽ thấy khó mở nhiều lần cùng một lúc và bạn sẽ cần phải tạo một hàm duy nhất để tạo, chỉnh sửa, lưu và đóng tài liệu. Phân bổ không gian trên heap cho phép bạn tạo bao nhiêu tài liệu theo ý muốn, mỗi tài liệu có kích thước phù hợp với dữ liệu chứa, và tránh buộc thời gian tồn tại của tài liệu vào tuổi thọ của bất kỳ chức năng cụ thể nào.

Tóm tắt: Tóm lại, ngăn xếp giữ giá trị của biến (đôi khi đăng ký được sử dụng để thay thế), trong khi vùng được sử dụng để phân bổ bộ nhớ sẽ được sử dụng vượt quá tuổi thọ của khối hiện tại.

+0

Bạn có thể cho tôi một ví dụ về một số điểm mà tôi buộc phải sử dụng heap không? Ví dụ, tôi chỉ có thể phân bổ tất cả mọi thứ trên ngăn xếp mà tôi cần cho toàn bộ chương trình trong hàm cha và truyền mọi thứ theo địa chỉ cho các hàm con. EDIT: hãy bỏ qua rằng ngăn xếp bị giới hạn bởi bất kỳ thứ gì khác ngoài bộ nhớ RAM đang đầy, vì lợi ích của câu hỏi. – xcrypt

+0

@xcrypt Điều đó đòi hỏi bạn phải biết trước về mọi phân bổ bộ nhớ mà chương trình của bạn có thể thực hiện. Cách khác, bạn có thể phân bổ một khối khổng lồ trên ngăn xếp của bạn mà sau đó bạn có thể cấp phát bộ nhớ động. Khối đó sẽ là chức năng tương đương của một đống. Tôi sẽ thêm một ví dụ ở trên. – Caleb

+0

Bạn đã đề cập rằng trình biên dịch tính toán số lượng không gian ngăn xếp cần thiết trước khi chạy, phải không? Điều đó có đúng với trường hợp đệ quy không? Bởi vì nếu đúng vậy thì trình biên dịch có thể nhắc một đệ trình vô hạn ngay sau khi biên dịch mã? – Dubby