2012-04-24 23 views
6

Một câu hỏi theo dõi để Memory leaks when calling ITK from Visual Studio DLLVS2010 báo cáo rò rỉ bộ nhớ sai cho các lớp học tĩnh trong một DLL

tôi tinh chế các vấn đề với ví dụ đơn giản nhất.

struct A 
    { 
    public: 
    A() 
     { 
     mp_data = new int(0x42); 
     } 
    ~A() 
     { 
     delete mp_data; 
     } 
    int* mp_data; 
    }; 

A a; 

Khi một lớp toàn cầu được định nghĩa trong một DLL, Visual Studio gỡ lỗi CRT báo cáo mp_data bị rò rỉ khi tắt ứng dụng. Có ai biết một workaround ngoại trừ vô hiệu hóa báo cáo rò rỉ?

+0

Đây có thể là lỗi Visual Studio. Xem các bình luận dưới câu trả lời được chấp nhận [ở đây] (http://stackoverflow.com/questions/2204608/does-c-call-destructors-for-global-and-class-static-variables). –

+0

Không, đó không phải là lý do. Tôi đã xác minh trong trình gỡ lỗi mà '~ A()' thực sự được gọi. – Andrey

Trả lời

5

Nếu bạn đang gọi _CrtDumpMemoryLeaks() ở cuối chức năng chính, hành vi được mong đợi, vì mp_data sẽ bị xóa sau khi _CrtDumpMemoryLeaks() được gọi.

Bạn sẽ cần gọi _CrtDumpMemoryLeaks() sau lần hủy cuối cùng của đối tượng tĩnh đã được gọi (hoặc đúng hơn trong lần hủy cuối cùng sau khi bộ nhớ đã được giải phóng) nếu bạn không muốn thấy những rò rỉ này. thử nó).

Phương pháp sạch hơn là để phân bổ tất cả các đối tượng tĩnh của bạn trên heap thay vì (vào đầu main), và deallocate họ vào cuối main, và sau đó bạn có thể gọi _CrtDumpMemoryLeaks() và sẽ không thấy bất kỳ rò rỉ bộ nhớ.

Các đối tượng tĩnh FYI với các hàm tạo và hàm hủy được coi là anyways xấu, bởi vì thứ tự mà chúng được xây dựng/desctructed không xác định, và vì các đối tượng tĩnh thường giới thiệu lỗi mà không thể gỡ lỗi dễ dàng.

Chỉnh sửa liên quan đến bình luận của Andrey: Bạn có thể thử để tắt cuộc gọi tự động để _CrtDumpMemoryLeaks bằng cách gọi _CrtSetDbgFlag để bỏ đặt _CRTDBG_LEAK_CHECK_DF cờ. Nếu điều đó làm việc, bạn có thể thêm một đối tượng tĩnh gọi _CrtDumpMemoryLeaks() trong destructor của nó. Để đảm bảo rằng đối tượng này bị hủy cuối cùng, bạn có thể sử dụng #pragma init_seg(compiler) directive.

Không có đầu mối nếu điều này có hiệu quả ... ngoài ra, tất cả các giải pháp khác rất có thể sẽ yêu cầu bạn sửa đổi thư viện ITK (có thể là thư viện nguồn mở sau này ?!).

+0

Điều đó không hoàn toàn áp dụng được, bởi vì (1) tôi không gọi _CrtDumpMemoryLeaks() bản thân mình, thư viện thời gian chạy VS làm điều đó cho tôi; (2) trên thực tế, các đối tượng tĩnh nằm trong lib bên thứ 3 nằm ngoài tầm kiểm soát của tôi (theo liên kết trong câu hỏi) – Andrey

+0

@Andrey: xem chỉnh sửa của tôi – smerlin

+0

Cảm ơn bạn! – Andrey

2

Bất kỳ cách nào sau đây giải quyết được sự cố.

(1) Tạo một phụ thuộc giả của DLL trên MFC, hoặc

(2) Sử dụng các giải pháp được đề xuất bởi smerlin: thêm mã này bên cạnh DllMain

struct _DEBUG_STATE 
    { 
    _DEBUG_STATE() {} 
    ~_DEBUG_STATE() { _CrtDumpMemoryLeaks(); } 
    }; 

#pragma init_seg(compiler) 
_DEBUG_STATE ds; 
2

tôi nhấn triệu chứng tương tự trong quá trình di chuyển một thư viện nội bộ từ liên kết tĩnh đến liên kết động thời gian tải, và hóa ra vấn đề trong trường hợp của tôi là dự án DLL và dự án EXE được liên kết với các phiên bản khác nhau của thư viện runtime/MFC của VC++. MBCS và một là Unicode).

Trong trường hợp của tôi, ứng dụng và thư viện đều sử dụng MFC và trình phá hủy _AFX_DEBUG_STATE kích hoạt lỗ rò bộ nhớ CRT được gọi hai lần, cho hai đối tượng riêng biệt - vì DLL và EXE được liên kết với các DLL thời gian chạy khác nhau, trạng thái tĩnh trong thời gian chạy đã được nhân đôi một cách hiệu quả. Một trong những DLL sẽ dỡ bỏ và đổ rò rỉ quá sớm và hiển thị một loạt các rò rỉ giả. Chuyển đổi cả hai dự án để sử dụng cùng một bộ ký tự đã giải quyết liên kết thời gian chạy riêng biệt và cũng đã giải quyết các báo cáo rò rỉ giả.

Trong trường hợp của tôi, liên kết đến hai thời gian chạy riêng biệt không chủ ý và có thể đã gây ra các sự cố khác. Điều này rõ ràng sẽ không phải là trường hợp khi tiêu thụ thư viện của bên thứ ba với một ABI được xác định rõ ràng nơi bạn không thể kiểm soát CRT thư viện được liên kết đến.

Không chắc chắn liệu điều này có thể áp dụng được trong trường hợp của bạn hay không nhưng tôi muốn đăng trong trường hợp nó hữu ích cho người khác.

0

Trong các ứng dụng MFC bạn có thể vô hiệu hóa tự động bộ nhớ bị rò rỉ bãi rác bằng cách gọi:

AfxEnableMemoryLeakDump(FALSE); 

này được hỗ trợ từ Visual Studio 2010. Đối với các tài liệu, xem here.

0

Tôi đã gặp vấn đề tương tự trong Visual Studio 2015. Tôi đã thử tất cả các giải pháp. Giải pháp đầu tiên với giả-MFC-phụ thuộc chỉ làm việc nếu bạn chọn tùy chọn trình biên dịch /MT trong dll của bạn. Vì vậy, dll của bạn và ứng dụng chính sẽ không chia sẻ cùng một đống. Nhưng thường cần có /MD, ví dụ: nếu bạn muốn chia sẻ các container STL hoặc các đối tượng chuỗi giữa Dll và ứng dụng chính của bạn (Dll-boundary). Nếu /MD được sử dụng, ứng dụng và Dll sử dụng cùng một đống. Vì vậy, giải pháp đầu tiên với giả-MFC-phụ thuộc không làm việc cho tôi.

Tôi không thích giải pháp thứ hai bằng cách tắt phát hiện rò rỉ bộ nhớ trong ứng dụng chính. Khi bạn không cần dll với cuộc gọi này trong destructor nữa, bạn phải nhớ để kích hoạt lại bộ nhớ phát hiện rò rỉ trong ứng dụng của bạn.

Tôi đã tìm thấy giải pháp khác để tôi không bị rò rỉ bộ nhớ giả nữa. Bạn chỉ phải sử dụng tùy chọn liên kết /delayload cho Dll của mình! Đó là tất cả :-). Nó làm việc cho tôi cũng với tùy chọn biên dịch /MD.

Here bạn có thể đọc nội dung nào đó về Ranh giới ranh giới (lý do sử dụng /MD?). Và here bạn có thể đọc điều gì đó về các tùy chọn trình biên dịch CRT nói chung.

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