Khi bạn thoát khỏi một ứng dụng C, bộ nhớ malloc-ed có tự động được giải phóng không?


92

Giả sử tôi có mã C sau:

int main () {
  int *p = malloc(10 * sizeof *p);
  *p = 42;
  return 0;  //Exiting without freeing the allocated memory
}

Khi tôi biên dịch và thực thi chương trình C đó, tức là sau khi cấp phát một số không gian trong bộ nhớ, liệu bộ nhớ mà tôi đã cấp phát có còn được cấp phát (tức là về cơ bản chiếm dung lượng) sau khi tôi thoát ứng dụng và quá trình kết thúc không?


9
đó là "phong cách tốt" để dọn dẹp bộ nhớ của bạn, không phải vì bạn có thể chạy trên hệ điều hành không có bộ nhớ được bảo vệ (đó là gợi ý chính bên dưới), mà vì nó làm tăng khả năng bạn tìm thấy bộ nhớ bị rò rỉ và giữ mã của bạn tinh gọn và chính xác ...
Matt Joiner

Câu trả lời:


111

Nó phụ thuộc vào hệ điều hành. Phần lớn các hệ điều hành hiện đại (và tất cả các hệ điều hành chính) sẽ giải phóng bộ nhớ không được giải phóng bởi chương trình khi nó kết thúc.

Dựa vào điều này là một cách làm không tốt và tốt hơn là bạn nên giải phóng nó một cách rõ ràng. Vấn đề không chỉ là mã của bạn trông xấu. Bạn có thể quyết định muốn tích hợp chương trình nhỏ của mình vào một chương trình lớn hơn, chạy lâu dài. Sau đó, một thời gian sau, bạn phải dành hàng giờ để theo dõi các rò rỉ bộ nhớ.
Việc dựa vào một tính năng của hệ điều hành cũng làm cho mã kém di động hơn.


16
Tôi đã từng gặp win98 trên nền tảng nhúng và dựa trên kinh nghiệm đó, tôi có thể nói rằng nó KHÔNG giải phóng bộ nhớ khi các chương trình đóng lại.
San Jacinto

8
@Ken Đó là một ví dụ. Ngoài ra, có một ranh giới giữa YAGNI và mã hóa cẩu thả. Không giải phóng tài nguyên vượt qua nó. Nguyên tắc YAGNI cũng được áp dụng cho các tính năng, không phải mã giúp chương trình hoạt động chính xác. (Và không giải phóng bộ nhớ là một lỗi).
Yacoby

5
+1: điều quan trọng nhất cần xem xét là quản lý bộ nhớ giống như Yacoby đã tuyên bố khá chính xác: "một tính năng của hệ điều hành" . Trừ khi tôi nhầm, ngôn ngữ lập trình không xác định những gì xảy ra trước hoặc sau khi thực hiện chương trình.
D.Shawley

9
Việc giải phóng bộ nhớ theo cách thủ công mất nhiều thời gian hơn, tốn nhiều mã hơn và có khả năng xuất hiện lỗi (hãy cho tôi biết bạn chưa bao giờ thấy lỗi trong mã phân bổ!). Không phải là "cẩu thả" khi cố ý bỏ qua một cái gì đó tồi tệ hơn theo mọi cách cho trường hợp sử dụng cụ thể của bạn. Trừ khi hoặc cho đến khi bạn có ý định chạy nó trên một số hệ thống cổ xưa / nhỏ bé không thể giải phóng các trang sau khi kết thúc quá trình hoặc tích hợp nó vào một chương trình lớn hơn (YAGNI), thì tôi coi như thua lỗ ròng. Tôi biết điều đó làm tổn thương cái tôi của một lập trình viên khi nghĩ đến việc không tự dọn dẹp nó, nhưng theo cách thực tế thì nó thực sự tốt hơn?
Ken

6
Bất kỳ ai đề xuất rò rỉ bộ nhớ trên SO nên bị tước bỏ tất cả danh tiếng và huy hiệu
Ulterior

53

Nói chung, các hệ điều hành đa năng hiện đại sẽ dọn dẹp sau khi các quá trình bị chấm dứt . Điều này là cần thiết vì giải pháp thay thế là để hệ thống mất tài nguyên theo thời gian và yêu cầu khởi động lại do các chương trình được viết kém hoặc đơn giản là có các lỗi hiếm khi xảy ra làm rò rỉ tài nguyên.

Dù sao thì chương trình của bạn cũng giải phóng tài nguyên rõ ràng có thể là một phương pháp hay vì nhiều lý do, chẳng hạn như:

  • Nếu bạn có các tài nguyên bổ sung không được HĐH dọn dẹp khi thoát, chẳng hạn như tệp tạm thời hoặc bất kỳ loại thay đổi nào đối với trạng thái của tài nguyên bên ngoài, thì bạn sẽ cần mã để giải quyết tất cả những thứ đó khi thoát và điều này thường được kết hợp trang nhã với việc giải phóng bộ nhớ.
  • Nếu chương trình của bạn bắt đầu có thời gian tồn tại lâu hơn, thì bạn sẽ không muốn cách duy nhất để giải phóng bộ nhớ là thoát. Ví dụ: bạn có thể muốn chuyển đổi chương trình của mình thành một máy chủ (daemon) tiếp tục chạy trong khi xử lý nhiều yêu cầu cho từng đơn vị công việc hoặc chương trình của bạn có thể trở thành một phần nhỏ của một chương trình lớn hơn.

Tuy nhiên, đây là một lý do để bỏ qua việc giải phóng bộ nhớ: tắt máy hiệu quả . Ví dụ: giả sử ứng dụng của bạn chứa một bộ nhớ đệm lớn trong bộ nhớ. Nếu khi thoát nó đi qua toàn bộ cấu trúc bộ nhớ cache và giải phóng từng phần một, điều đó không phục vụ mục đích hữu ích và lãng phí tài nguyên. Đặc biệt, hãy xem xét trường hợp các trang bộ nhớ chứa bộ nhớ cache của bạn đã được hệ điều hành hoán đổi sang đĩa; bằng cách di chuyển cấu trúc và giải phóng nó, bạn sẽ đưa tất cả các trang đó trở lại bộ nhớ cùng một lúc , lãng phí thời gian và năng lượng đáng kể mà không mang lại lợi ích thực tế nào và thậm chí có thể khiến các chương trình khác trên hệ thống bị hoán đổi!

Như một ví dụ liên quan, có những máy chủ hiệu suất cao hoạt động bằng cách tạo một quy trình cho mỗi yêu cầu, sau đó thoát ra khi hoàn thành; bằng cách này có nghĩa là họ thậm chí không phải theo dõi phân bổ bộ nhớ và không bao giờ thực hiện bất kỳ giải phóng hoặc thu thập rác nào, vì mọi thứ chỉ biến mất trở lại bộ nhớ trống của hệ điều hành khi kết thúc quá trình. (Điều tương tự có thể được thực hiện trong một quy trình sử dụng trình cấp phát bộ nhớ tùy chỉnh, nhưng yêu cầu lập trình rất cẩn thận; về cơ bản là đưa ra khái niệm của riêng mình về “quy trình nhẹ” trong quy trình hệ điều hành).


11

Lời xin lỗi của tôi vì đã đăng rất lâu sau khi bài cuối cùng đến chủ đề này.

Một điểm bổ sung. Không phải tất cả các chương trình đều có thể thoát ra được. Sự cố và ctrl-C, v.v. sẽ khiến một chương trình thoát ra theo những cách không kiểm soát được. Nếu hệ điều hành của bạn không giải phóng đống của bạn, dọn dẹp ngăn xếp, xóa các biến tĩnh, v.v., cuối cùng bạn sẽ khiến hệ thống của mình bị hỏng do rò rỉ bộ nhớ hoặc tệ hơn.

Ngoài điều thú vị này, sự cố / sự cố trong Ubuntu và tôi nghi ngờ tất cả các hệ điều hành hiện đại khác, đều có vấn đề với tài nguyên "được xử lý". Các ổ cắm, tệp, thiết bị, v.v. có thể vẫn "mở" khi chương trình kết thúc / gặp sự cố. Đó là cũng có thể thực hành tốt để đóng bất kỳ thứ gì bằng "tay cầm" hoặc "bộ mô tả" như một phần của quá trình dọn dẹp của bạn trước khi thoát ra.

Tôi hiện đang phát triển một chương trình sử dụng nhiều ổ cắm. Khi tôi bị mắc kẹt, tôi phải ctrl-c ra khỏi nó, do đó, mắc kẹt các ổ cắm của tôi. Tôi đã thêm một vectơ std :: để thu thập danh sách tất cả các ổ cắm đã mở và một trình xử lý sigaction bắt sigint và sigterm. Trình xử lý đi qua danh sách và đóng các ổ cắm. Tôi dự định thực hiện một thói quen dọn dẹp tương tự để sử dụng trước khi ném sẽ dẫn đến việc chấm dứt sớm.

Bất cứ ai quan tâm để bình luận về thiết kế này?


1
Tôi rất vui vì bạn đã nói điều này, bởi vì tôi đã có một chương trình để lại tài nguyên ổ cắm xung quanh và hệ thống Ubuntu của chúng tôi cần khởi động lại hai tuần một lần hoặc bộ nhớ bắt đầu cạn kiệt và có nhiều bộ nhớ. Tôi không chắc tài nguyên hệ thống bị phá hủy nếu bạn quên dọn dẹp chúng.
octopusgrabbus

8
Stackoverflow không phải là một diễn đàn; không có sai khi trả lời một câu hỏi cũ. meta.stackexchange.com/questions/20524/reviving-old-questions
mk12,

6

Điều đang xảy ra ở đây ( trong một hệ điều hành hiện đại ), là chương trình của bạn chạy bên trong "quy trình" của chính nó. Đây là một thực thể hệ điều hành được ưu đãi với không gian địa chỉ riêng, bộ mô tả tệp, v.v. Các malloclệnh gọi của bạn đang cấp phát bộ nhớ từ "heap" hoặc các trang bộ nhớ chưa phân bổ được gán cho quy trình của bạn.

Khi chương trình của bạn kết thúc, như trong ví dụ này, tất cả các tài nguyên được chỉ định cho quy trình của bạn chỉ được tái chế / chia nhỏ bởi hệ điều hành. Trong trường hợp bộ nhớ, tất cả các trang bộ nhớ được gán cho bạn chỉ đơn giản được đánh dấu là "miễn phí" và được tái chế để sử dụng cho các quy trình khác. Trang là một khái niệm cấp thấp hơn so với những gì malloc xử lý - kết quả là, các chi tiết cụ thể của malloc / free đều bị xóa sạch khi toàn bộ nội dung được dọn dẹp.

Nó tương đương với đạo đức, khi bạn sử dụng xong máy tính xách tay của mình và muốn tặng nó cho bạn bè, bạn không cần phải xóa từng tệp riêng lẻ. Bạn chỉ cần định dạng ổ cứng.

Tất cả những điều này đã nói, như tất cả những người trả lời khác đều lưu ý, dựa vào điều này là không tốt:

  1. Bạn nên luôn lập trình để quản lý tài nguyên và trong C điều đó có nghĩa là bộ nhớ. Bạn có thể kết thúc việc nhúng mã của mình vào thư viện hoặc nó có thể chạy lâu hơn bạn mong đợi.
  2. Một số hệ điều hành (những hệ điều hành cũ hơn và có thể một số hệ điều hành được nhúng hiện đại) có thể không duy trì các ranh giới quy trình khó khăn như vậy và việc phân bổ của bạn có thể ảnh hưởng đến không gian địa chỉ của những người khác.

4

Đúng. Hệ điều hành dọn dẹp tài nguyên. Chà ... các phiên bản cũ của NetWare thì không.

Chỉnh sửa: Như San Jacinto đã chỉ ra, chắc chắn có những hệ thống (ngoài NetWare) không làm điều đó. Ngay cả trong các chương trình bỏ đi, tôi cố gắng tạo thói quen giải phóng mọi tài nguyên chỉ để duy trì thói quen.


3
Tôi không phản đối, nhưng đây là một bài đăng khá nguy hiểm cho hậu thế. DOS vẫn được sử dụng trên nhiều nền tảng nhúng, và tôi NGHI NGỜ nghi ngờ rằng nó có thể dọn dẹp bộ nhớ cho bạn. Sự khái quát bao quát là sai.
San Jacinto

@San Jacinto: Đó là một điểm tốt. Đó là lý do tại sao tôi thực hiện tham chiếu NetWare, nhưng nó có thể sử dụng làm rõ. Tôi sẽ chỉnh sửa nó một chút.
Mark Wilkins

3
@San DOS không phải là hệ điều hành đa tác vụ - khi chương trình DOS (không bao gồm TSR) kết thúc, tất cả bộ nhớ sẽ có sẵn để tải chương trình tiếp theo.

@Neil cảm ơn bạn đã nhắc nhở, nhưng tôi đang đề cập đến một chương trình giống TSR sẽ khởi chạy khi sự kiện xảy ra, cũng như cách sử dụng phổ biến cho các hệ thống nhúng. Tuy nhiên, cảm ơn bạn vì kiến ​​thức chuyên môn của bạn và làm rõ tôi đã thất bại ở đâu :)
San Jacinto

2

Có, hệ điều hành giải phóng tất cả bộ nhớ khi quá trình kết thúc.


Tôi không hiểu tại sao điều này lại bị phản đối. bộ nhớ malloc'ed sẽ được giải phóng khi quá trình chết (wikipedia definiton của malloc nói như vậy)
Arve

7
Wikipedia không phải là cẩm nang cho mọi hệ điều hành đang tồn tại. Hầu hết các hệ điều hành hiện đại sẽ lấy lại bộ nhớ, nhưng không phải tất cả (và đặc biệt là không phải tất cả các hệ điều hành cũ) đều làm như vậy. Thêm vào đó, mallocchỉ có thể hứa những gì C sẽ làm với bộ nhớ; theo thiết kế, C không đảm bảo bất cứ điều gì liên quan đến hành vi bên ngoài C. Nếu ứng dụng chết bất ngờ, bất kỳ lời hứa nào mà thư viện thời gian chạy thực hiện đều vô hiệu, vì nó không còn tồn tại để sống theo chúng.
cHao

2

Nó phụ thuộc, hệ điều hành thường sẽ dọn dẹp nó cho bạn, nhưng nếu bạn đang làm việc trên phần mềm nhúng, ví dụ như phần mềm nhúng thì nó có thể không được phát hành.

Chỉ cần đảm bảo rằng bạn giải phóng nó, nó có thể giúp bạn tiết kiệm rất nhiều thời gian sau này khi bạn có thể muốn tích hợp nó vào một dự án lớn.


0

Điều đó thực sự phụ thuộc vào hệ điều hành, nhưng đối với tất cả các hệ điều hành bạn sẽ gặp phải, phân bổ bộ nhớ sẽ biến mất khi quá trình thoát.


0

Tôi nghĩ rằng miễn phí trực tiếp là tốt nhất. Hành vi không xác định là điều tồi tệ nhất, vì vậy nếu bạn có quyền truy cập trong khi nó vẫn được xác định trong quy trình của bạn, hãy làm điều đó, có rất nhiều lý do chính đáng mà mọi người đã đưa ra cho nó.

Đối với việc ở đâu, hoặc liệu, tôi thấy rằng trong W98, câu hỏi thực sự là 'khi nào' (tôi không thấy bài đăng nào nhấn mạnh điều này). Một chương trình mẫu nhỏ (đối với đầu vào MIDI SysEx, sử dụng các khoảng trống khác nhau) sẽ giải phóng bộ nhớ trong bit WM_DESTROY của WndProc, nhưng khi tôi cấy nó vào một chương trình lớn hơn, nó đã bị lỗi khi thoát. Tôi cho rằng điều này có nghĩa là tôi đang cố gắng giải phóng những gì Hệ điều hành đã giải phóng trong một đợt dọn dẹp lớn hơn. Nếu tôi thực hiện trên WM_CLOSE, sau đó được gọi là DestroyWindow (), tất cả đều hoạt động tốt, thoát ngay lập tức.

Mặc dù điều này không hoàn toàn giống với bộ đệm MIDI, nhưng có điểm giống nhau ở chỗ tốt nhất là giữ nguyên quy trình, dọn dẹp đầy đủ, sau đó thoát. Với phần bộ nhớ khiêm tốn, điều này rất nhanh. Tôi thấy rằng nhiều bộ đệm nhỏ hoạt động và dọn dẹp nhanh hơn so với ít bộ đệm lớn hơn.

Có thể tồn tại các ngoại lệ, như ai đó đã nói khi tránh lấy lại các khối bộ nhớ lớn từ tệp hoán đổi trên đĩa, nhưng thậm chí điều đó có thể được giảm thiểu bằng cách giữ nhiều hơn và nhỏ hơn, không gian được cấp phát.

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.