Hay nó giống như "việc xử lý các đối tượng trong C ++ thực sự rất khó khăn - tôi dành 20% thời gian cho nó và tuy nhiên, rò rỉ bộ nhớ là một nơi phổ biến"?
Theo kinh nghiệm cá nhân của tôi về C ++ và thậm chí C, rò rỉ bộ nhớ chưa bao giờ là một cuộc đấu tranh lớn để tránh. Ví dụ, với quy trình kiểm tra lành mạnh và Valgrind, mọi rò rỉ vật lý do cuộc gọi đến operator new/malloc
mà không có tương ứng delete/free
thường được phát hiện và sửa chữa nhanh chóng. Công bằng mà nói, một số cơ sở mã C ++ lớn hoặc trường học cũ rất có thể có một số trường hợp cạnh khó hiểu có thể rò rỉ vật lý một số byte bộ nhớ ở đây và do đó không phải deleting/freeing
là trường hợp cạnh đó đã bay theo radar thử nghiệm.
Tuy nhiên, theo như những quan sát thực tế, các ứng dụng rò rỉ nhất mà tôi gặp phải (như trong những ứng dụng tiêu thụ càng nhiều bộ nhớ thì bạn càng chạy chúng lâu hơn, mặc dù lượng dữ liệu chúng tôi làm việc không tăng lên) thường không được viết bằng C hoặc C ++. Tôi không tìm thấy những thứ như Linux Kernel hoặc Unreal Engine hoặc thậm chí mã gốc được sử dụng để triển khai Java trong danh sách các phần mềm bị rò rỉ mà tôi gặp phải.
Loại phần mềm rò rỉ nổi bật nhất mà tôi có xu hướng gặp phải là những thứ như Flash applet, như trò chơi Flash, mặc dù chúng sử dụng bộ sưu tập rác. Và đó không phải là một so sánh công bằng nếu người ta suy luận bất cứ điều gì từ điều này vì nhiều ứng dụng Flash được viết bởi các nhà phát triển vừa chớm nở, những người có thể thiếu các nguyên tắc kỹ thuật âm thanh và quy trình thử nghiệm (và tương tự như vậy, tôi chắc chắn có những chuyên gia lành nghề làm việc với GC không đấu tranh với phần mềm bị rò rỉ), nhưng tôi sẽ có rất nhiều điều để nói với bất cứ ai nghĩ rằng GC ngăn chặn phần mềm bị rò rỉ được viết.
Con trỏ
Bây giờ đến từ miền, kinh nghiệm cụ thể của tôi và vì hầu hết sử dụng C và C ++ (và tôi hy vọng lợi ích của GC sẽ thay đổi tùy thuộc vào kinh nghiệm và nhu cầu của chúng tôi), điều ngay lập tức nhất mà GC giải quyết cho tôi không phải là vấn đề rò rỉ bộ nhớ thực tế mà là truy cập con trỏ lơ lửng, và đó thực sự có thể là cứu cánh trong các tình huống quan trọng.
Thật không may trong nhiều trường hợp trong đó GC giải quyết điều gì khác sẽ là truy cập con trỏ lơ lửng, nó thay thế cùng một loại lỗi lập trình viên bằng rò rỉ bộ nhớ logic.
Nếu bạn tưởng tượng rằng trò chơi Flash được viết bởi một lập trình viên mới, anh ta có thể lưu trữ các tham chiếu đến các yếu tố trò chơi trong nhiều cấu trúc dữ liệu, khiến chúng chia sẻ quyền sở hữu các tài nguyên trò chơi này. Thật không may, giả sử anh ta đã phạm sai lầm khi anh ta quên loại bỏ các yếu tố trò chơi khỏi một trong các cấu trúc dữ liệu khi chuyển sang giai đoạn tiếp theo, ngăn không cho chúng được giải phóng cho đến khi toàn bộ trò chơi bị tắt. Tuy nhiên, trò chơi vẫn hoạt động tốt vì các yếu tố không được rút ra hoặc ảnh hưởng đến tương tác của người dùng. Tuy nhiên, trò chơi bắt đầu sử dụng ngày càng nhiều bộ nhớ hơn trong khi tốc độ khung hình tự hoạt động với trình chiếu, trong khi quá trình xử lý ẩn vẫn lặp qua bộ sưu tập các yếu tố ẩn trong trò chơi này (hiện đã trở nên bùng nổ về kích thước). Đây là loại vấn đề tôi gặp phải thường xuyên trong các trò chơi Flash như vậy.
- Tôi đã gặp những người nói rằng điều này không được tính là "rò rỉ bộ nhớ" bởi vì bộ nhớ vẫn đang được giải phóng khi đóng ứng dụng, và thay vào đó có thể được gọi là "rò rỉ không gian" hoặc một cái gì đó cho hiệu ứng này. Mặc dù sự phân biệt như vậy có thể hữu ích để xác định và nói về các vấn đề, tôi không thấy sự khác biệt đó rất hữu ích trong bối cảnh này nếu chúng ta nói về nó giống như nó không phải là vấn đề như "rò rỉ bộ nhớ" khi chúng ta giao dịch mục tiêu thực tế của việc đảm bảo phần mềm không làm tăng số lượng bộ nhớ vô lý khi chúng ta chạy nó lâu hơn (trừ khi chúng ta đang nói về các hệ điều hành tối nghĩa không giải phóng bộ nhớ của quá trình khi nó bị chấm dứt).
Bây giờ hãy nói rằng cùng một nhà phát triển vừa chớm nở đã viết trò chơi bằng C ++. Trong trường hợp đó, thông thường sẽ chỉ có một cấu trúc dữ liệu trung tâm trong trò chơi "sở hữu" bộ nhớ trong khi các cấu trúc khác chỉ vào bộ nhớ đó. Nếu anh ta mắc một lỗi tương tự, rất có thể, khi tiến tới giai đoạn tiếp theo, trò chơi sẽ bị sập do truy cập các con trỏ lơ lửng (hoặc tệ hơn, làm một việc khác ngoài sự cố).
Đây là loại đánh đổi ngay lập tức nhất mà tôi có xu hướng gặp phải trong miền của mình thường xuyên nhất giữa GC và không có GC. Và tôi thực sự không quan tâm đến GC rất nhiều trong lĩnh vực của mình, điều này không quá quan trọng, bởi vì những cuộc đấu tranh lớn nhất tôi từng gặp phải với phần mềm bị rò rỉ liên quan đến việc sử dụng GC trong một nhóm cũ gây ra rò rỉ được mô tả ở trên .
Trong miền cụ thể của tôi, tôi thực sự thích phần mềm bị sập hoặc bị trục trặc trong nhiều trường hợp bởi vì điều đó ít nhất dễ phát hiện hơn là cố gắng tìm ra lý do tại sao phần mềm tiêu thụ một cách bí ẩn lượng bộ nhớ sau khi chạy được nửa giờ trong khi tất cả chúng ta các bài kiểm tra đơn vị và tích hợp vượt qua mà không có khiếu nại (thậm chí từ Valgrind, vì bộ nhớ đang được giải phóng bởi GC khi tắt máy). Tuy nhiên, đó không phải là một cú đánh vào GC của tôi hay cố gắng nói rằng nó vô dụng hay bất cứ thứ gì tương tự, nhưng nó không phải là bất kỳ loại đạn bạc nào, thậm chí không gần gũi, trong các đội tôi làm việc chống lại phần mềm bị rò rỉ (để ngược lại tôi đã có trải nghiệm ngược lại với việc một codebase sử dụng GC là rò rỉ nhất tôi từng gặp). Công bằng mà nói, nhiều thành viên trong nhóm đó thậm chí còn không biết tài liệu tham khảo yếu là gì,
Sở hữu chung và Tâm lý học
Vấn đề tôi gặp phải với bộ sưu tập rác có thể khiến nó dễ bị "rò rỉ bộ nhớ" (và tôi sẽ khăng khăng gọi nó như là 'rò rỉ không gian' hoạt động theo cách chính xác theo quan điểm của người dùng) những người không sử dụng nó một cách cẩn thận liên quan đến "khuynh hướng của con người" ở một mức độ nào đó theo kinh nghiệm của tôi. Vấn đề với nhóm đó và cơ sở mã hóa rò rỉ nhất mà tôi từng gặp là họ dường như bị ấn tượng rằng GC sẽ cho phép họ ngừng suy nghĩ về việc ai sở hữu tài nguyên.
Trong trường hợp của chúng tôi, chúng tôi đã có rất nhiều đối tượng tham chiếu lẫn nhau. Các mô hình sẽ tham chiếu các tài liệu cùng với thư viện vật liệu và hệ thống đổ bóng. Tài liệu sẽ tham khảo kết cấu cùng với thư viện kết cấu và các shader nhất định. Máy ảnh sẽ lưu trữ các tham chiếu đến tất cả các loại thực thể cảnh cần được loại trừ khỏi kết xuất. Danh sách dường như tiếp tục vô tận. Điều đó đã tạo ra bất kỳ nguồn tài nguyên khổng lồ nào trong hệ thống được sở hữu và kéo dài suốt đời ở hơn 10 nơi khác trong trạng thái ứng dụng cùng một lúc, và điều đó rất, rất dễ bị lỗi của con người có thể chuyển thành rò rỉ (và không một vấn đề nhỏ, tôi đang nói về gigabyte trong vài phút với các vấn đề về khả năng sử dụng nghiêm trọng). Về mặt khái niệm, tất cả các tài nguyên này không cần phải chia sẻ quyền sở hữu, về mặt khái niệm chúng đều có một chủ sở hữu,
Nếu chúng ta ngừng suy nghĩ về việc ai sở hữu bộ nhớ nào, và vui vẻ chỉ lưu trữ các tài liệu tham khảo kéo dài suốt đời cho các đối tượng ở khắp mọi nơi mà không nghĩ về điều này, thì phần mềm sẽ không gặp sự cố do con trỏ lơ lửng nhưng gần như chắc chắn, theo như vậy suy nghĩ bất cẩn, bắt đầu rò rỉ bộ nhớ như điên theo những cách rất khó để theo dõi và sẽ trốn tránh các bài kiểm tra.
Nếu có một lợi ích thiết thực cho con trỏ lơ lửng trong miền của tôi, thì đó là nó gây ra sự cố và sự cố rất khó chịu. Và điều đó ít nhất có xu hướng khuyến khích các nhà phát triển, nếu họ muốn gửi một thứ gì đó đáng tin cậy, để bắt đầu suy nghĩ về quản lý tài nguyên và thực hiện những điều thích hợp cần thiết để loại bỏ tất cả các tham chiếu / con trỏ bổ sung cho một đối tượng không còn cần thiết về mặt khái niệm.
Quản lý tài nguyên ứng dụng
Quản lý tài nguyên phù hợp là tên của trò chơi nếu chúng ta đang nói về việc tránh rò rỉ trong các ứng dụng tồn tại lâu với trạng thái liên tục được lưu trữ trong đó việc rò rỉ sẽ gây ra các vấn đề nghiêm trọng về tốc độ khung hình và khả năng sử dụng. Và quản lý tài nguyên chính xác ở đây không kém phần khó khăn khi có hoặc không có GC. Công việc cũng không kém phần thủ công để loại bỏ các tham chiếu thích hợp đến các đối tượng không còn cần thiết cho dù chúng là con trỏ hay tham chiếu kéo dài suốt đời.
Đó là thách thức trong miền của tôi, không quên delete
những gì chúng tôi new
(trừ khi chúng tôi đang nói chuyện nghiệp dư với thử nghiệm, thực hành và công cụ kém chất lượng). Và nó đòi hỏi phải suy nghĩ và quan tâm xem chúng ta có sử dụng GC hay không.
Đa luồng
Một vấn đề khác tôi thấy rất hữu ích với GC, nếu nó có thể được sử dụng rất thận trọng trong miền của tôi, là để đơn giản hóa việc quản lý tài nguyên trong các bối cảnh đa luồng. Nếu chúng ta cẩn thận không lưu trữ các tham chiếu kéo dài suốt đời tới các tài nguyên ở nhiều nơi trong trạng thái ứng dụng, thì bản chất kéo dài trọn đời của các tham chiếu GC có thể cực kỳ hữu ích như một cách để các luồng tạm thời mở rộng tài nguyên được truy cập để mở rộng thời gian tồn tại của nó chỉ trong một khoảng thời gian ngắn khi cần thiết để xử lý xong.
Tôi nghĩ rằng việc sử dụng GC rất cẩn thận theo cách này có thể mang lại một phần mềm rất chính xác, không bị rò rỉ, đồng thời đơn giản hóa đa luồng.
Có nhiều cách xung quanh điều này mặc dù vắng mặt GC. Trong trường hợp của tôi, chúng tôi thống nhất biểu diễn thực thể cảnh của phần mềm, với các luồng tạm thời khiến tài nguyên cảnh được mở rộng trong thời gian ngắn theo cách khá khái quát trước giai đoạn dọn dẹp. Điều này có thể có mùi hơi giống với GC nhưng sự khác biệt là không có "quyền sở hữu chung" liên quan, chỉ có một thiết kế xử lý cảnh thống nhất trong các luồng xử lý phá hủy các tài nguyên nói trên. Tuy nhiên, sẽ đơn giản hơn nhiều nếu chỉ dựa vào GC ở đây nếu nó có thể được sử dụng rất cẩn thận với các nhà phát triển có lương tâm, cẩn thận sử dụng các tài liệu tham khảo yếu trong các lĩnh vực liên tục có liên quan, cho các trường hợp đa luồng như vậy.
C ++
Cuối cùng:
Trong C ++, tôi phải gọi xóa để loại bỏ một đối tượng được tạo vào cuối vòng đời của nó.
Trong Modern C ++, đây thường không phải là thứ bạn nên làm thủ công. Nó thậm chí không quá nhiều về việc quên làm điều đó. Khi bạn liên quan đến việc xử lý ngoại lệ vào hình ảnh, thì ngay cả khi bạn đã viết một delete
cuộc gọi tương ứng bên dưới một số cuộc gọi đến new
, một cái gì đó có thể ném vào giữa và không bao giờ thực hiện delete
cuộc gọi nếu bạn không dựa vào các cuộc gọi hủy tự động được trình biên dịch chèn vào để thực hiện việc này bạn.
Với C ++, thực tế bạn cần phải làm như vậy, trừ khi bạn làm việc trong một bối cảnh được nhúng với các ngoại lệ và các thư viện đặc biệt được lập trình một cách có chủ ý không ném, tránh việc dọn dẹp tài nguyên thủ công như vậy (bao gồm tránh các cuộc gọi thủ công để mở khóa mutex bên ngoài dtor , ví dụ, và không chỉ giải quyết bộ nhớ). Xử lý ngoại lệ đòi hỏi khá nhiều, vì vậy hầu hết việc dọn dẹp tài nguyên nên được tự động hóa thông qua các bộ hủy.