Bao nhiêu sử dụng ngăn xếp là quá nhiều?


22

Gần đây khi tôi viết C hoặc C ++, tôi sẽ khai báo tất cả các biến của mình trên ngăn xếp chỉ vì đó là một tùy chọn, không giống với Java.

Tuy nhiên, tôi đã nghe nói rằng đó là một ý tưởng tồi để khai báo những thứ lớn trên ngăn xếp.

  1. Tại sao chính xác là trường hợp này? Tôi cho rằng stack stack có liên quan, nhưng tôi không rõ tại sao điều đó xảy ra.
  2. Bao nhiêu thứ trên stack là quá nhiều?

Tôi không cố gắng đặt các tệp 100 MB vào ngăn xếp, chỉ là một tá mảng kilobyte để sử dụng làm bộ đệm chuỗi hoặc bất cứ thứ gì. Đây có phải là quá nhiều sử dụng ngăn xếp?

(Xin lỗi nếu trùng lặp, việc tìm kiếm ngăn xếp tiếp tục đưa ra các tham chiếu đến Stack Overflow. Thậm chí không có thẻ ngăn xếp cuộc gọi, tôi chỉ sử dụng thẻ trừu tượng.)


1
Làm thế nào để bạn "đặt các tệp 100 MB trên ngăn xếp"? Việc triển khai bộ đệm và vùng chứa (và tương tự như std :: string) thường sử dụng heap để lưu trữ trọng tải của chúng.
Murphy

2
Bạn có thể thoát khỏi một lượng sử dụng ngăn xếp hợp lý cho mỗi chức năng / phương pháp cho đến khi đệ quy có liên quan, sau đó bạn đang mạo hiểm hạn chế nghiêm trọng khả năng của mình, độ sâu đệ quy, vì vậy trong các hàm đệ quy, bạn muốn sử dụng ít cục bộ biến / ngăn xếp không gian càng tốt.
Erik Eidt

3
Lưu ý rằng C & C ++ là khác nhau. Một std::vector<int>biến cục bộ sẽ không ăn nhiều dung lượng ngăn xếp, hầu hết dữ liệu đang được chất đống.
Basile Starynkevitch

Câu trả lời:


18

Nó phụ thuộc vào hệ điều hành của bạn. Trên Windows, kích thước tối đa điển hình cho ngăn xếp là 1MB, trong khi đó là 8 MB trên Linux hiện đại điển hình, mặc dù các giá trị đó được điều chỉnh theo nhiều cách khác nhau. Nếu tổng các biến ngăn xếp của bạn (bao gồm cả chi phí cấp thấp như địa chỉ trả về, đối số dựa trên ngăn xếp, giữ chỗ giá trị trả về và byte căn chỉnh) trong toàn bộ ngăn xếp cuộc gọi vượt quá giới hạn đó, bạn sẽ bị tràn ngăn xếp, thường làm giảm chương trình mà không có bất kỳ cơ hội phục hồi.

Một vài kilobyte thường là tốt. Hàng chục kilobyte là nguy hiểm vì nó bắt đầu tổng hợp. Hàng trăm kilobyte là một ý tưởng rất tồi.


1
Không phải là ngăn xếp điển hình giới hạn vài megabyte (thường là nhiều hơn một, nhưng có lẽ ít hơn một chục) ngày hôm nay trong năm 2016? Trên máy tính để bàn Linux của tôi, nó là 8Mbyte theo mặc định ...
Basile Starynkevitch

"Trên [...] Linux, kích thước tối đa điển hình cho một ngăn xếp là 1MB" $ ulimit -atrên hệ thống của tôi trả về cho những người khác stack size (kbytes, -s) 8192.
Murphy

9

Câu trả lời hợp lệ duy nhất là mơ hồ: "quá nhiều là khi ngăn xếp tràn ra."

Trừ khi bạn hoàn toàn kiểm soát việc triển khai mọi dòng mã giữa điểm vào của chương trình và hàm được đề cập, bạn không thể đưa ra giả định nào về số lượng ngăn xếp có sẵn. Ví dụ, bạn không thể đảm bảo rằng việc gọi hàm này sẽ không bao giờ gây ra lỗi tràn ngăn xếp:

void break_the_camels_back()
{
    int straw;
    ...
}

Ngăn xếp 8 MiB mặc định trên các Unix hiện đại có khá nhiều chỗ trống, đặc biệt là với những người như tôi, người đủ để nhớ CPU với con trỏ ngăn xếp 8 bit. Thực tế là bạn khó có thể vượt qua nó mà không thử. Nếu bạn làm như vậy, vượt quá giới hạn ngăn xếp thường được coi là vi phạm phân khúc và các hệ thống có đủ quản lý bộ nhớ để phát hiện nó sẽ gửi SIGSEGVkhi xảy ra.

Bạn có một vài lựa chọn. Đầu tiên là không đoán được bao nhiêu stack có sẵn và hỏi hệ thống. Bất cứ điều gì phù hợp với POSIX sẽ có getrlimit(2)chức năng cho bạn biết giới hạn trên. RLIMIT_STACKlà giới hạn cụ thể mà bạn muốn. Thứ hai là theo dõi số lượng chương trình của bạn đang sử dụng và đưa ra quyết định về các biến tự động so với phân bổ bộ nhớ động dựa trên đó. Theo tôi biết, không có chức năng tiêu chuẩn nào để xác định số lượng ngăn xếp được sử dụng, nhưng các chương trình như valgrindcó thể phân tích nó cho bạn.


4

Nếu bạn phân bổ một mảng gồm 10.000 byte trên ngăn xếp, thì mảng đó bị giới hạn về kích thước. 10.000 có thể là rất nhiều, nhưng nếu bạn cần 10,001 byte thì chương trình của bạn có thể bị sập hoặc tệ hơn. Vì vậy, trong tình huống này, bạn muốn thứ gì đó phù hợp với kích thước bạn cần và thứ gì đó sẽ không nằm trong ngăn xếp.

Các mảng kích thước cố định cho bộ đệm chuỗi trên ngăn xếp không phải là vấn đề vì chúng giữ bộ nhớ trên ngăn xếp, chúng là một vấn đề vì bộ đệm kích thước cố định là một vấn đề nghiêm trọng đang chờ xảy ra.

Nhưng nếu bạn sử dụng C ++ và khai báo ví dụ std :: string hoặc std :: vec trên stack, thì những gì trên stack sẽ thực sự có kích thước cố định và nhỏ. Dữ liệu thực tế sẽ được lưu trữ trên heap. Bạn có thể lưu trữ một triệu ký tự trong trường hợp chuỗi std :: và nó sẽ chỉ lấy một lượng dữ liệu rất nhỏ (thường là 8 đến 24 byte, tùy thuộc vào việc triển khai) trên ngăn xếp và một triệu byte trên heap.


2

Vâng 1 MB là một ước tính tốt cho * nix. Đệ quy có thể là một lý do chính cho tràn ngăn xếp kết hợp với phân bổ ngăn xếp. Tuy nhiên, trong hầu hết các trường hợp, các đối tượng thần có vẻ bề ngoài quá lớn được đặt trên ngăn xếp được thiết kế tốt để quản lý bộ nhớ trong của chúng trên đống và chỉ sử dụng ngăn xếp như một cách để tự động bị phá hủy khi ngăn xếp được bật lên. Kẻ hủy diệt sẽ giải phóng khối lượng lớn bộ nhớ được quản lý nội bộ. Các thùng chứa tiêu chuẩn được thiết kế theo cách đó và các con trỏ chia sẻ / duy nhất cũng được thiết kế theo cách đó.

Điều quan trọng là không phân bổ các khối lớn mem thô trên stack như char [1024 * 1024] và thiết kế các lớp để bọc phân bổ heap và chỉ sử dụng stack để thuận tiện cho việc tự động gọi hàm hủy.

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.