2012-04-14 38 views
10

Tôi đang lập hồ sơ của một trò chơi tôi đã viết và tôi tự hỏi làm thế nào có thể đoạn mã sau đây gây ra sự gia tăng heap 4kb (tôi đang lược tả bằng Heapshot Analysis of Xcode) mỗi khi nó được thực hiện:Rò rỉ gây ra bởi fread

u8 WorldManager::versionOfMap(FILE *file) 
{ 
    char magic[4]; 
    u8 version; 

    fread(magic, 4, 1, file); <-- this is the line 
    fread(&version,1,1,file); 
    fseek(file, 0, SEEK_SET); 

    return version; 
} 

Theo profiler dòng nhấn mạnh phân bổ 4.00Kb bộ nhớ với một malloc mỗi khi hàm được gọi, bộ nhớ mà không bao giờ được phát hành. Điều này dường như xảy ra với các cuộc gọi khác tới số fread xung quanh mã, nhưng đây là mã được gọi là eclatant nhất.

Tôi có thiếu điều gì tầm thường không? Có nội dung nào đó mà tôi không nên quan tâm?

Cũng giống như ghi chú: Tôi đang lược tả trên iPhone và được biên dịch dưới dạng bản phát hành (-O2).

+4

điều gì xảy ra khi bạn thay đổi thành 'fread (magic, 1, 4, file);'? – Yahia

+0

Bạn đã kiểm tra lỗi đọc (ví dụ: với 'ferror (tệp)')? – Beta

+0

một điểm khác: kiểm tra kết quả của cuộc gọi 'fread'! – Yahia

Trả lời

3

Nếu những gì bạn mô tả thực sự đang diễn ra và mã của bạn không có lỗi ở nơi khác, đó là lỗi trong quá trình triển khai, tôi nghĩ vậy.

Nhiều khả năng tôi nghĩ, là khả năng bạn không đóng tệp. Luồng Stdio sử dụng bộ đệm theo mặc định nếu thiết bị không tương tác và bộ đệm được cấp phát vào lúc tệp được mở hoặc khi I/O được thực hiện. Trong khi chỉ có một bộ đệm nên được cấp phát, bạn chắc chắn có thể rò rỉ bộ đệm bằng cách quên đóng tệp. Nhưng chắc chắn, đóng tập tin nên giải phóng bộ đệm. Đừng quên kiểm tra giá trị được trả về bởi fclose.

Giả sử vì lợi ích của đối số rằng bạn đóng đúng tệp có một vài phần tử khác trong mã của bạn sẽ không gây ra vấn đề này nhưng tôi sẽ đề cập đến.

Trước tiên, hãy gọi fread cuộc gọi đọc đối tượng có một thành viên có kích thước 4. Bạn thực sự có một đối tượng có 4 thành viên có kích thước 1. Nói cách khác, đối số dạng số là fread được hoán đổi. Điều này tạo ra sự khác biệt chỉ theo ý nghĩa của giá trị trả về (quan trọng trong trường hợp đọc một phần).

Thứ hai, trong khi cuộc gọi đầu tiên của bạn để fread đúng cứng mã kích thước của char như 1 (trong C, đó là định nghĩa về 'kích thước'), nó có thể là tốt hơn về phong cách sử dụng sizeof(u8) trong cuộc gọi thứ hai để fread.

Nếu ý tưởng này thực sự là rò rỉ bộ nhớ là giải thích chính xác (và không có bất kỳ lỗi nào ở nơi khác) thì bạn có thể khắc phục sự cố bằng cách tắt bộ đệm stdio cho tệp cụ thể này:

bool WorldManager::versionOfMap(FILE *file, bool *is_first_file_io, u8 *version) 
{ 
    char magic[4]; 
    bool ok = false; 
    if (*is_first_file_io) 
    { 
    // we ignore failure of this call 
    setvbuf(file, NULL, _IONBF, 0); 
    *is_first_file_io = false; 
    } 

    if (sizeof(magic) == fread(magic, 1, sizeof(magic), file) 
     && 1 == fread(version, sizeof(*version), 1, file)) 
    { 
     ok = true; 
    } 
    if (-1 == fseek(file, 0L, SEEK_SET)) 
    { 
     return false; 
    } 
    else 
    { 
     return ok && 0 == memcmp(magic, EXPECTED_MAGIC, sizeof(magic)); 
    } 
} 

Ngay cả khi chúng tôi đi với giả thuyết rằng đây thực sự là lỗi, và sự rò rỉ là có thật, nó cũng có giá trị ngưng tụ mã của bạn thành ví dụ nhỏ nhất có thể vẫn giải thích được vấn đề. Nếu làm điều đó cho thấy lỗi thực sự, bạn giành chiến thắng. Nếu không, bạn sẽ cần ví dụ tối thiểu để báo cáo lỗi trong quá trình triển khai.

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