2013-05-28 35 views
8

xem xét mã C sau đây, mà tạo ra 100.000 trang 4KB cỡ, sau đó giải phóng 99.999 trang và, cuối cùng, giải phóng trang cuối cùng:Memory rò rỉ trên phân bổ trang với malloc

#include <stdio.h> 
#include <stdlib.h> 

#define NUM_PAGES 100000 

int main() { 
    void *pages[NUM_PAGES]; 

    int i; 
    for(i=0; i<NUM_PAGES; i++) { 
     pages[i] = malloc(4096); 
    } 

    printf("%d pages allocated.\n", NUM_PAGES); 
    getchar(); 

    for(i=0; i<NUM_PAGES-1; i++) { 
     free(pages[i]); 
    } 

    printf("%d pages freed.\n", NUM_PAGES-1); 
    getchar(); 

    free(pages[NUM_PAGES-1]); 

    printf("Last page freed.\n"); 
    getchar(); 

    return 0; 
} 

Nếu bạn biên dịch nó, chạy nó và theo dõi quá trình sử dụng bộ nhớ, bạn có thể thấy rằng việc sử dụng bộ nhớ đạt khoảng 400MB trước khi getchar đầu tiên (khi bộ nhớ được cấp cho 100.000 trang), sau đó nó giữ nguyên ngay cả sau khi 99.999 trang được phân bổ (sau giây getchar) và cuối cùng, nó giảm xuống 1MB khi trang cuối cùng được phân bổ.

Vì vậy, câu hỏi của tôi là tại sao điều này lại xảy ra? Tại sao toàn bộ bộ nhớ chỉ trả về hệ điều hành khi tất cả các trang được giải phóng? Có bất kỳ kích thước trang nào hay bất kỳ sự sắp xếp trang nào ngăn cản điều này xảy ra không? Tôi có nghĩa là, là có bất kỳ kích thước trang hoặc liên kết làm cho bất kỳ trang malloced được hoàn toàn trả lại cho hệ điều hành khi chỉ có một trang được giải thoát?

+3

Điều này xảy ra vì các phân bổ quá nhỏ nên thư viện C của bạn sử dụng 'sbrk()' để tự động thay đổi kích thước dữ liệu chưa được khởi tạo của nó. Nó là tuần tự, do đó, nó chỉ có thể được thu nhỏ khi phân bổ mới nhất được giải phóng. Nếu bạn tăng kích thước phân bổ để nói 131072 byte (128k), 'strace' cho thấy nó sử dụng' mmap() 'thay cho phân bổ, và mỗi' free() 'thực sự trả về phân bổ cho hệ điều hành. Vì vậy, hãy sử dụng bộ nhớ cache cấp phát và chỉ yêu cầu/trả về các khối lớn hơn cho Hệ điều hành. –

+0

@NominalAnimal Man, anh đã cứu mạng tôi! Cảm ơn bạn rất nhiều cho các ý kiến ​​ở trên và dưới đây. Đó là chính xác những gì tôi đã mong đợi từ các câu trả lời. Nếu bạn đã trả lời (không bình luận) điều này, tôi đã chấp nhận câu trả lời của bạn ... – LuisABOL

Trả lời

5

Điều này hoàn toàn phụ thuộc vào việc triển khai thực hiện, nhưng tôi tin rằng điều này phải làm với cách bộ phân bổ bộ nhớ hoạt động. Thông thường, khi người quản lý bộ nhớ cần nhiều bộ nhớ hơn từ hệ điều hành, nó gọi hàm sbrk để yêu cầu bộ nhớ bổ sung. Việc thực hiện điển hình của chức năng này là hệ điều hành lưu trữ một con trỏ đến địa chỉ miễn phí tiếp theo trong bộ nhớ, nơi quá trình có thể nhận được không gian. Bộ nhớ tăng lên giống như một chồng, giống như cách mà ngăn xếp cuộc gọi hoạt động. Ví dụ, nếu bạn phân bổ năm trang của bộ nhớ, nó có thể trông như thế này:

(existing memory) | Page 0 | Page 1 | Page 2 | Page 3 | Page 4 | (next free spot) 

Với thiết lập này, nếu bạn trang miễn phí 0-4, người quản lý bộ nhớ bên trong chương trình sẽ đánh dấu chúng là miễn phí, như thế này :

(existing memory) |         | Page 4 | (next free spot) 

Vì hệ điều hành phân bổ bộ nhớ theo kiểu xếp chồng, nên không thể lấy lại tất cả bộ nhớ này từ chương trình cho đến khi hoàn tất việc sử dụng. Một khi bạn giải phóng các trang cuối cùng, bộ nhớ của quá trình sẽ giống như thế này:

(existing memory) |            (next free spot) 

Và vào thời điểm này quản lý bộ nhớ của chương trình có thể trở lại mà số tiền rất lớn của không gian miễn phí cho hệ điều hành:

(existing memory) | (next free spot) 

Nói cách khác, bởi vì bộ nhớ được phân bổ như là một ngăn xếp, cho đến khi bạn deallocate điều cuối cùng bạn đã được phân bổ, hệ điều hành không thể đòi lại bất kỳ bộ nhớ.

Hy vọng điều này sẽ hữu ích!

+0

Ok, cảm ơn bạn rất nhiều vì đã trả lời! Giải thích rất tốt về phân bổ bộ nhớ. Nhưng, tôi muốn biết liệu bạn có biết cách nào để tránh phân bổ bộ nhớ giống như chồng hay không, ý tôi là, phân bổ các trang riêng lẻ được phân bổ riêng lẻ. – LuisABOL

+2

@ LuisAntonioBotelhoO.Leite Bạn có thể sẽ cần các cuộc gọi phụ thuộc vào hệ điều hành để nhận/giải phóng bộ nhớ từ hệ điều hành trực tiếp. Trong các cửa sổ bạn sử dụng [VirtualAlloc] (http://msdn.microsoft.com/en-us/library/windows/desktop/aa366887 (v ​​= vs.85) .aspx) và [VirtualFree] (http: // msdn. microsoft.com/en-us/library/windows/máy tính để bàn/aa366892 (v = vs.85) .aspx). Trên unices, sử dụng [sbrk (2)] (http://linux.die.net/man/2/sbrk). Lưu ý rằng bạn được cảnh báo để chỉ sử dụng malloc/miễn phí và tránh sbrk. – Anthony

+0

@ anthony-arnold Cảm ơn bạn rất nhiều! – LuisABOL

Các vấn đề liên quan