2009-06-30 16 views
10

Tôi sử dụng boost :: shared_ptr trong ứng dụng của tôi trong C++. Vấn đề bộ nhớ thực sự nghiêm trọng và ứng dụng chiếm một lượng lớn bộ nhớ.Làm cách nào để biết ai đang giữ shared_ptr <>?

Tuy nhiên, vì tôi đặt mọi đối tượng mới vào shared_ptr, khi ứng dụng thoát, không thể phát hiện rò rỉ bộ nhớ.

Phải có một số thứ như std::vector<shared_ptr<> > hồ bơi giữ tài nguyên. Làm thế nào tôi có thể biết ai giữ shared_ptr, khi gỡ lỗi?

Thật khó để xem xét từng dòng mã. Quá nhiều mã ...

Cảm ơn rất nhiều!

Trả lời

20

Bạn không thể biết bằng cách chỉ xem shared_ptr, trong đó "con trỏ anh em" là. Bạn có thể kiểm tra xem một trong số đó là unique() hoặc nhận được use_count(), trong số other methods.

1

Bạn rõ ràng đang giữ tham chiếu đến các đối tượng trong ứng dụng của mình. Điều này có nghĩa là bạn đang cố ý giữ mọi thứ trong bộ nhớ. Điều đó có nghĩa là bạn không bị rò rỉ bộ nhớ. Một rò rỉ bộ nhớ là khi bộ nhớ được cấp phát, và sau đó bạn không giữ một tham chiếu đến địa chỉ của nó.

Về cơ bản, bạn cần xem xét thiết kế của mình và tìm ra lý do tại sao bạn đang lưu giữ quá nhiều đối tượng và dữ liệu trong bộ nhớ và cách bạn có thể giảm thiểu nó.

Một khả năng bạn có rò rỉ bộ nhớ giả là bạn đang tạo nhiều đối tượng hơn bạn nghĩ. Thử đặt các điểm ngắt trên tất cả các câu lệnh có chứa 'mới'. Xem ứng dụng của bạn có đang xây dựng nhiều đối tượng hơn bạn nghĩ hay không và sau đó đọc qua mã đó.

Vấn đề thực sự không phải là quá nhiều rò rỉ bộ nhớ vì đó là vấn đề về thiết kế của ứng dụng của bạn.

+1

Rất cám ơn! Có khoảng 200 nghìn dòng. Vì vậy, rất khó để kiểm tra mỗi mới ... là có bất kỳ Macro biên dịch để kích hoạt khả năng kiểm tra ref của tăng (nếu khả năng như vậy là tồn tại). Bộ nhớ gây ra bởi lỗi lập trình logic về các hồ bơi, tôi chắc chắn, nhưng tôi không thể tìm thấy nó. – user25749

+3

Bạn vẫn có thể bị rò rỉ bộ nhớ với shared_ptrs. Tạo một tham chiếu tuần hoàn và nó sẽ không bao giờ bị xóa, ngay cả khi phần còn lại của ứng dụng không còn tham chiếu đến nó nữa. Rò rỉ bộ nhớ tức thì! – jalf

+5

Một tham chiếu đến một đối tượng được giữ lại không chính xác vẫn là một rò rỉ tài nguyên. Đây là lý do tại sao các chương trình GC vẫn có thể bị rò rỉ, thường là do mẫu Observer - người quan sát nằm trong danh sách thay vì có thể quan sát và không bao giờ bị lấy đi. Cuối cùng, 'remove' là cần thiết cho mỗi' add', giống như 'delete' là cần thiết cho mỗi' new'. Chính xác cùng một lỗi lập trình, gây ra chính xác cùng một vấn đề. Một "tài nguyên" thực sự chỉ là một cặp hàm phải được gọi là số lần bằng với các đối số tương ứng và "rò rỉ tài nguyên" là những gì xảy ra khi bạn không thực hiện điều đó. –

3

Bạn có thể gặp phải rò rỉ bộ nhớ con trỏ dùng chung thông qua chu kỳ. Điều gì xảy ra là các đối tượng được chia sẻ của bạn có thể giữ tham chiếu đến các đối tượng được chia sẻ khác mà cuối cùng dẫn trở lại bản gốc. Khi điều này xảy ra, chu kỳ giữ tất cả các số tham chiếu ở mức 1 mặc dù không ai khác có thể truy cập các đối tượng. Giải pháp là weak pointers.

+0

Cảm ơn bạn rất nhiều! Tôi thực sự sử dụng weak_ptr làm trình theo dõi tài nguyên. Vì vậy, tôi biết số lượng lớn shared_ptr <> hiện có trong bộ nhớ. Tôi chắc chắn không có chu kỳ, một số mô-đun bị bệnh được thiết kế, tôi đang cố gắng tìm ra nó. – user25749

+2

"_Các giải pháp là con trỏ yếu._" Không có nó không phải là. Giải pháp là xem xét thiết kế. – curiousguy

+0

@curiousguy: Vâng vâng, để trở thành giải pháp _real_ cho người sở hữu theo chu kỳ là tránh nó. Tuy nhiên, có những vấn đề hợp pháp được giải quyết tốt hơn với con trỏ theo chu kỳ. Trong những trường hợp đó: Một giải pháp cho rò rỉ bộ nhớ do tham chiếu tuần hoàn thực sự cần thiết là sử dụng 'std :: weak_ptr'. –

3

Hãy thử tái cấu trúc một số mã của bạn để quyền sở hữu hiển thị rõ ràng hơn bằng cách sử dụng con trỏ yếu thay vì con trỏ được chia sẻ ở một số nơi.

Khi xem xét phân cấp lớp, bạn có thể xác định lớp nào cần giữ con trỏ được chia sẻ và chỉ cần trỏ yếu, vì vậy bạn có thể tránh chu kỳ nếu có và nếu đối tượng chủ sở hữu "thực" bị hủy , các đối tượng "không phải chủ sở hữu" đã bị mất. Nếu nó chỉ ra rằng một số đối tượng bị mất con trỏ quá sớm, bạn phải nhìn vào chuỗi phá hủy đối tượng trong ứng dụng của bạn và sửa chữa nó.

+0

Những gì bạn đang nói là ** tài liệu tham khảo yếu có thể được sử dụng để gỡ lỗi **: nếu tham chiếu yếu đã chết, chúng tôi cần sử dụng nó, có nghĩa là chương trình có lỗi. Đây là lần đầu tiên tôi thấy ý tưởng này được thể hiện rõ ràng trên hệ điều hành - một số áp phích khác sử dụng 'weak_ptr' dường như ngụ ý rằng nó là một công cụ gỡ lỗi, nhưng họ không nói rõ điều đó. Lưu ý: đó là ít hiệu quả hơn bằng cách sử dụng một con trỏ C++ thông thường: có lẽ chúng ta cần một 'checked_ptr' có thể được định nghĩa là con trỏ thông minh' weak_ptr' hoặc như một con trỏ thông minh "pointer pointer". – curiousguy

-1

Không thể biết đối tượng nào được chia sẻ shared_ptr từ bên trong chương trình. Nếu bạn đang sử dụng Linux, một cách chắc chắn để gỡ lỗi rò rỉ bộ nhớ là công cụ Valgrind - trong khi nó sẽ không trả lời trực tiếp câu hỏi của bạn, nó sẽ cho biết bộ nhớ được cấp phát ở đâu, thường đủ để sửa lỗi. Tôi tưởng tượng Windows có các công cụ tương đương, nhưng tôi không biết cái nào là tốt nhất.

11

Việc sử dụng phổ biến rộng rãi của shared_ptr sẽ gần như chắc chắn gây ra sự chiếm đóng bộ nhớ không mong muốn và không nhìn thấy.

Tham chiếu tuần hoàn là một nguyên nhân nổi tiếng và một số có thể gián tiếp và khó phát hiện đặc biệt là trong mã phức tạp được thực hiện bởi nhiều lập trình viên; một lập trình viên có thể quyết định hơn một đối tượng cần tham chiếu đến một đối tượng khác như một sửa chữa nhanh và không có thời gian để kiểm tra tất cả mã để xem liệu anh ta có đang đóng chu kỳ hay không. Nguy hiểm này được đánh giá rất thấp.

Ít được hiểu hơn là vấn đề của tài liệu tham khảo chưa được phát hành. Nếu một đối tượng được chia sẻ với nhiều shared_ptrs thì nó sẽ không bị hủy bỏ cho đến khi một đối tượng nào đó bị xóa đi hoặc không nằm ngoài phạm vi. Nó rất dễ dàng để bỏ qua một trong những tài liệu tham khảo và kết thúc với các đối tượng ẩn giấu trong bộ nhớ mà bạn nghĩ rằng bạn đã kết thúc với.

Mặc dù nghiêm túc nói rằng đây không phải là rò rỉ bộ nhớ (tất cả sẽ được phát hành trước khi thoát chương trình) chúng cũng nguy hiểm và khó phát hiện hơn.

Các sự cố này là hậu quả của việc khai báo sai sự thật: 1. Khai báo những gì bạn thực sự muốn là quyền sở hữu duy nhất là shared_ptr. scoped_ptr sẽ là chính xác nhưng sau đó bất kỳ tham chiếu nào khác đến đối tượng đó sẽ phải là một con trỏ thô, có thể bị treo lơ lửng. 2. Khai báo những gì bạn thực sự muốn trở thành một tham chiếu thụ động thụ động như shared_ptr. weak_ptr sẽ là chính xác nhưng sau đó bạn có những rắc rối của việc chuyển đổi nó để share_ptr mỗi khi bạn muốn sử dụng nó.

Tôi nghi ngờ rằng dự án của bạn là một ví dụ tốt về loại rắc rối mà thực tiễn này có thể đưa bạn vào.

Nếu bạn có ứng dụng chuyên sâu về bộ nhớ, bạn thực sự cần quyền sở hữu duy nhất để thiết kế của bạn có thể kiểm soát rõ ràng tuổi thọ đối tượng.

Với quyền sở hữu duy nhất opObject = NULL; chắc chắn sẽ xóa đối tượng và nó sẽ làm điều đó ngay bây giờ.

Với quyền sở hữu chia sẻ spObject = NULL; ........ ai biết? ......

+0

"_a lập trình viên có thể quyết định hơn một đối tượng cần tham chiếu đến một đối tượng khác như sửa chữa nhanh và không có thời gian để kiểm tra tất cả mã để xem liệu anh ấy có đang đóng chu kỳ không._" Anh ấy không phải "đọc mã" . Anh ấy nên đọc ** tài liệu thiết kế **. – curiousguy

+4

trong thế giới thực rất nhiều dự án không có khái niệm về tài liệu thiết kế :) – paulm

+3

@curiousguy Tôi thích mã để thiết kế tài liệu như tài liệu có thể nói dối và biên dịch mã. – M2tM

1

Tôi sẽ đề nghị sử dụng UMDH nếu bạn đang ở trên cửa sổ. Nó là một công cụ rất mạnh mẽ. Sử dụng nó tìm phân bổ cho mỗi giao dịch/khoảng thời gian mà bạn mong đợi để được giải phóng sau đó tìm thấy những người đang nắm giữ chúng.

Có nhiều thông tin về vấn đề này SO trả lời Find memory leaks caused by smart pointers

5

Một giải pháp cho treo lủng lẳng hoặc tham chiếu con trỏ thông minh tròn chúng tôi đã làm được tùy chỉnh lớp con trỏ thông minh để thêm một debug-chỉ có chức năng kế toán. Bất cứ khi nào một smartpointer thêm một tham chiếu đến một đối tượng, phải mất một stack trace và đặt nó trong một bản đồ mà mỗi mục theo dõi

  1. Địa chỉ của đối tượng được phân bổ (những gì mà con trỏ trỏ đến)
  2. các địa chỉ của mỗi smartpointer đối tượng giữ một tham chiếu đến đối tượng
  3. các stacktraces tương ứng của mỗi khi smartpointer được xây dựng

khi một smartpointer đi ra khỏi phạm vi, nhập cảnh của nó trên bản đồ bị xóa. Khi con trỏ thông minh cuối cùng đến một đối tượng bị phá hủy, đối tượng pointee sẽ nhận được mục nhập của nó trong bản đồ bị xóa.Sau đó, chúng tôi có lệnh "rò rỉ bản nhạc" với hai chức năng: '[re] bắt đầu theo dõi rò rỉ' (xóa toàn bộ bản đồ và theo dõi được bật nếu chưa có) và 'in tài liệu tham khảo mở', hiển thị tất cả tài liệu tham khảo smartpointer nổi bật được tạo ra kể từ khi lệnh 'bắt đầu theo dõi rò rỉ' được phát hành. Vì bạn có thể nhìn thấy dấu vết ngăn xếp nơi những con trỏ thông minh đó xuất hiện, bạn có thể dễ dàng biết chính xác ai đang giữ đối tượng của bạn được giải phóng. Nó làm chậm mọi thứ khi nó bật, vì vậy chúng tôi không để nó vào mọi lúc.

Đó là một số tiền hợp lý của công việc để thực hiện, nhưng chắc chắn giá trị nếu bạn đã có một codebase nơi điều này xảy ra rất nhiều.

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