eryksun đã trả lời câu hỏi # 1, và tôi đã trả lời câu hỏi # 3 (bản gốC# 4), nhưng bây giờ chúng ta hãy trả lời câu hỏi # 2:
Tại sao nó phát hành 50.5mb đặc biệt - Số tiền được phát hành dựa trên số tiền gì?
Điều gì dựa trên, cuối cùng, toàn bộ chuỗi trùng hợp bên trong Python và malloc
rất khó dự đoán.
Đầu tiên, tùy thuộc vào cách bạn đo lường bộ nhớ, bạn chỉ có thể đo các trang thực sự được ánh xạ vào bộ nhớ.Trong trường hợp đó, bất kỳ khi nào một trang được hoán đổi bởi máy nhắn tin, bộ nhớ sẽ hiển thị là "được giải phóng", mặc dù nó chưa được giải phóng. Hoặc bạn có thể đang đo lường các trang được sử dụng, có thể hoặc không thể đếm các trang được phân bổ nhưng không bao giờ chạm (trên các hệ thống phân bổ quá mức, như linux), các trang được phân bổ nhưng được gắn thẻ MADV_FREE
, v.v.
Nếu bạn thực sự đang đo các trang được phân bổ (thực sự không phải là điều rất hữu ích để làm, nhưng dường như đó là điều bạn đang hỏi), và các trang đã thực sự được phân bổ, hai trường hợp trong đó xảy ra: Hoặc bạn đã sử dụng brk
hoặc tương đương để thu hẹp phân đoạn dữ liệu (rất hiếm hiện nay) hoặc bạn đã sử dụng munmap
hoặc tương tự để phát hành phân đoạn được ánh xạ. (Ngoài ra, về mặt lý thuyết có một biến thể nhỏ về sau, trong đó có nhiều cách để giải phóng một phần của một phân đoạn được ánh xạ — ví dụ, ăn cắp nó với MAP_FIXED
cho phân đoạn MADV_FREE
mà bạn ngay lập tức unmap.)
Nhưng hầu hết các chương trình không trực tiếp phân bổ mọi thứ ra khỏi các trang bộ nhớ; họ sử dụng một phân bổ theo kiểu malloc
. Khi bạn gọi free
, người cấp phát chỉ có thể phát hành các trang cho hệ điều hành nếu bạn chỉ xảy ra là free
nhập đối tượng trực tiếp cuối cùng trong ánh xạ (hoặc trong các trang N cuối cùng của đoạn dữ liệu). Không có cách nào ứng dụng của bạn có thể dự đoán hợp lý điều này, hoặc thậm chí phát hiện ra rằng nó đã xảy ra trước.
CPython làm cho việc này trở nên phức tạp hơn — nó có bộ phân bổ đối tượng 2 cấp tùy chỉnh ở trên cùng của bộ cấp phát bộ nhớ tùy chỉnh ở trên cùng của malloc
. (Xem the source comments để có giải thích chi tiết hơn.) Và trên hết, ngay cả ở cấp C API, ít hơn nhiều Python, bạn thậm chí không trực tiếp kiểm soát khi các đối tượng cấp cao nhất được deallocated.
Vì vậy, khi bạn phát hành một đối tượng, làm thế nào để bạn biết liệu nó sẽ phát hành bộ nhớ cho hệ điều hành? Trước tiên, bạn phải biết rằng bạn đã phát hành tham chiếu cuối cùng (bao gồm bất kỳ tham chiếu nội bộ nào mà bạn không biết), cho phép GC giải quyết nó. Điều này thường giải quyết ít nhất hai điều ở cấp độ tiếp theo xuống (ví dụ, đối với một chuỗi, bạn đang phát hành đối tượng PyString
và chuỗi đó đệm).
Nếu bạn làm deallocate một đối tượng, để biết liệu điều này có làm giảm cấp độ tiếp theo để deallocate một khối lưu trữ đối tượng hay không, bạn phải biết trạng thái bên trong của phân bổ đối tượng. (Nó rõ ràng là không thể xảy ra trừ khi bạn đang deallocating điều cuối cùng trong khối, và thậm chí sau đó, nó có thể không xảy ra.)
Nếu bạn làm deallocate một khối lưu trữ đối tượng, để biết liệu điều này gây ra một cuộc gọi free
, bạn phải biết trạng thái bên trong của trình phân bổ PyMem, cũng như cách nó được thực hiện. (Một lần nữa, bạn phải được deallocating cuối cùng khối trong sử dụng trong một khu vực malloc
ed, và thậm chí sau đó, nó có thể không xảy ra.)
Nếu bạn làmfree
một vùng malloc
ed, để biết liệu nguyên nhân này an munmap
hoặc tương đương (hoặc brk
), bạn phải biết trạng thái nội bộ của malloc
, cũng như cách được triển khai. Và cái này, không giống như những cái khác, là đặc trưng cho nền tảng cao. (Và một lần nữa, bạn thường phải được deallocating cuối cùng trong sử dụng malloc
trong một phân đoạn mmap
, và thậm chí sau đó, nó có thể không xảy ra.)
Vì vậy, nếu bạn muốn hiểu tại sao nó xảy ra để phát hành chính xác 50.5mb , bạn sẽ phải theo dõi nó từ dưới lên.Tại sao malloc
giá trị unmap 50,5mb của trang khi bạn thực hiện một hoặc nhiều cuộc gọi free
(có thể nhiều hơn một chút so với 50,5mb)? Bạn sẽ phải đọc malloc
nền tảng của bạn, và sau đó đi bộ các bảng và danh sách khác nhau để xem trạng thái hiện tại của nền tảng. (Trên một số nền tảng, nó thậm chí có thể sử dụng thông tin cấp hệ thống, mà không thể chụp được mà không tạo ảnh chụp nhanh của hệ thống để kiểm tra ngoại tuyến, nhưng may mắn thay đây không phải là vấn đề.) Và sau đó bạn phải làm điều tương tự ở 3 cấp trên.
Vì vậy, câu trả lời hữu ích duy nhất cho câu hỏi là "Bởi vì".
Trừ khi bạn đang thực hiện phát triển tài nguyên giới hạn (ví dụ: được nhúng), bạn không có lý do gì để quan tâm đến các chi tiết này.
Và nếu bạn là phát triển nguồn lực hạn chế, việc biết các chi tiết này là vô dụng; bạn khá nhiều phải làm một kết thúc xung quanh tất cả các cấp và đặc biệt là mmap
bộ nhớ bạn cần ở cấp ứng dụng (có thể với một đơn giản, được hiểu rõ, ứng dụng cụ thể vùng phân bổ ở giữa).
Cần lưu ý rằng hành vi này không cụ thể đối với Python. Nó thường là trường hợp đó, khi một quá trình giải phóng một số bộ nhớ phân bổ heap, bộ nhớ không được phát hành trở lại hệ điều hành cho đến khi quá trình chết. – NPE
Câu hỏi của bạn yêu cầu nhiều thứ - một số trong số đó là những con dups, một số trong số đó không phù hợp với SO, một số trong số đó có thể là câu hỏi hay. Bạn đang hỏi liệu Python có phát hành bộ nhớ hay không, dưới những hoàn cảnh chính xác mà nó có thể/không thể, cơ chế cơ bản là gì, tại sao nó được thiết kế theo cách đó, cho dù có cách giải quyết nào hay cái gì khác hoàn toàn? – abarnert
@abarnert Tôi kết hợp các truy vấn con tương tự. Để trả lời các câu hỏi của bạn: Tôi biết Python phát hành một số bộ nhớ cho hệ điều hành nhưng tại sao không phải tất cả của nó và tại sao số tiền mà nó làm. Nếu có những hoàn cảnh không thể, tại sao? Cách giải quyết khác là gì. – Jared