5

Hầu hết các lập trình viên đồng ý rằng việc thu gom rác là một điều tuyệt vời, và trong hầu hết các ứng dụng đều đáng giá. Tuy nhiên, quan sát cá nhân của tôi là việc quản lý bộ nhớ cho hầu hết các đối tượng là tầm thường, và có lẽ 10% -20% trong số chúng giải thích cho nhu cầu về kludges như đếm tham chiếu và các lược đồ quản lý bộ nhớ phức tạp nói chung. Có vẻ như tôi có thể nhận được tất cả lợi ích của việc thu gom rác chỉ bằng một phần nhỏ chi phí bằng cách xóa các đối tượng lớn một cách thủ công, nơi mà tuổi thọ của đối tượng hiển nhiên và cho phép GC thu thập phần còn lại, giả sử triển khai GC hỗ trợ . Điều này sẽ cho phép GC chạy ít thường xuyên hơn và tiêu thụ ít bộ nhớ dư thừa hơn, trong khi vẫn tránh các trường hợp thực sự khó quản lý theo cách thủ công. Thậm chí thú vị hơn sẽ là nếu trình biên dịch chèn lệnh delete xác định tự động nơi kiếp đã rõ ràng:Quy tắc 90/10 về quản lý bộ nhớ?

int myFunc() { 
    Foo[] foo = new Foo[arbitraryNumber]; // May be too big to stack allocate. 
    // do stuff such that the compiler can prove foo doesn't escape. 
    // foo is obviously no longer needed, can be automatically deleted here. 
    return someInteger; 
} 

Tất nhiên, điều này có thể không làm việc tốt với một GC sao chép, nhưng vì lợi ích của bài này chúng ta hãy giả isn GC của chúng tôi không sao chép. Tại sao các lược đồ quản lý bộ nhớ lai như vậy dường như rất hiếm trong các ngôn ngữ lập trình chính thống?

+1

Xác nhận âm đạo về 'quan sát cá nhân' không giúp các lập trình viên khác. Bạn đã * đo gì *? –

Trả lời

3

Vì trường hợp này quá hiếm. Hầu như không có phương pháp nào bị cô lập. Tất cả đều chấp nhận các đối tượng từ bên ngoài hoặc tạo ra các đối tượng và vượt qua chúng.

Phương thức không truy cập vào bất kỳ trường nào, không có tham số và không trả lại nội dung nào đó không thể làm gì cả.

Thay vào đó, các GC tập trung vào trường hợp phổ biến nhất (90%) và cố gắng giữ 90% (các đối tượng tạm thời ngắn ngủi) trong kiểm tra. Điều này có nghĩa là trong trường hợp thông thường, bạn có ít đối tượng hơn để kiểm tra và phần còn lại không quan trọng lắm. Tiếp theo, bạn sử dụng một lần quét tăng dần (vì vậy bạn có thể chạy trong các lần chạy nước rút nhỏ chỉ làm gián đoạn trong một thời gian ngắn).

Tôi đã từng cố gắng đưa ra thuật toán GC tốt hơn và thất bại thảm hại. Họ sử dụng một cách tiếp cận biên giới trên arcane. Tài liệu về Java 5 GC Performance Tuning sẽ cung cấp cho bạn một số ý tưởng. Và dĩ nhiên là có GC article in Wikipedia.

Nội dung tóm tắt: Sử dụng GC thậm chí có thể nhanh hơn việc phân bổ bộ nhớ truyền thống và lược đồ giải phóng. Hãy suy nghĩ về thuật toán cổ điển mà chỉ cần đặt bất kỳ đối tượng có thể truy cập và sao chép nó vào một nơi mới. Nếu bạn vừa quên về rất nhiều đối tượng (ví dụ, 90% của tất cả các đối tượng được phân bổ), thuật toán này chỉ cần kiểm tra phần còn lại (10%). Bất cứ điều gì mà nó không thể đạt được, không có vấn đề bao nhiêu có thể được, sẽ không quan trọng. Bây giờ bạn có thể nói rằng sao chép là tốn kém nhưng a) điều này là không đúng; ngày nay một CPU máy tính để bàn trung bình có thể sao chép 40MB trong vòng chưa đầy 100ms và b) sao chép sẽ bảo vệ bạn chống phân mảnh, vì vậy nó thực sự là một điều tốt .

+0

Ok, sai lầm ngớ ngẩn. Tôi đã cơ bản ngụ ý một hàm thuần túy, nhưng hàm thuần túy sẽ trả về một cái gì đó. Đã sửa. – dsimcha

+0

Kiểm tra tất cả mã nguồn bạn đã viết trong cuộc sống của mình. Tôi nghi ngờ rằng bạn sẽ tìm thấy nhiều phương pháp thực hiện điều gì đó hữu ích * và * không hoạt động trên các đối tượng từ một phạm vi bên ngoài. Ngoài ra, như tôi đã giải thích, các vật thể sống đắt tiền, các vật thể chết đều miễn phí. –

+1

Theo kinh nghiệm của tôi, nhà phát triển ứng dụng thậm chí không biết một nửa số phân bổ họ tạo ra.Có lẽ bạn chỉ không nhận thức được những trường hợp rác thải tầm thường vì bạn đã không nhận ra bao nhiêu rác bạn đã thực hiện. – Aaron

1

Một lưu ý nhanh chóng trên "rõ ràng là không còn cần thiết": Nó không phải là dễ dàng;)

[...] 
Foo() { 
    someGlobalList.add(this); 
} 
[...] 

Ngoài ra, ý tưởng của bạn về khả năng tự xóa thingies lớn là imo một tốt nhất. Như tôi đã hiểu, ít nhất nó được triển khai một phần bằng các ngôn ngữ hiện đại (ví dụ: using trong C#, thật đáng buồn là không thực sự miễn phí). Tuy nhiên, không đến mức độ bạn muốn.

+0

có, nhưng hãy xem xét trường hợp của "chuỗi s1, s2, s3; ... s1 + s2 + s3". Bộ nhớ được cấp phát trong quá trình ghép nối của s1 và s2 sẽ là rác trước khi kết thúc biểu thức này, được đảm bảo. - Sự hiểu biết của tôi là Lisps khác nhau đã có những loại tối ưu hóa. – Aaron

1

Những gì bạn mô tả như các câu lệnh xóa xác định cho các đối tượng không thoát hiện đại GC thực hiện khá hiệu quả. Hầu hết các đối tượng được cấp phát được phân bổ từ các nhóm và bị xóa rất hiệu quả khi thoát khỏi phương thức - rất ít kết thúc trên vùng heap GC chậm hơn.

Điều này có hiệu lực tạo ra hiệu ứng tương tự như bạn mô tả với ít can thiệp của lập trình viên hơn.Và bởi vì một nhà sưu tập rác tự động cho phép phạm vi ít hơn cho lỗi của con người (nếu bạn xóa một cái gì đó mà một tài liệu tham khảo vẫn được tổ chức).

0

Nếu bạn đang tạo đối tượng mà bạn mong đợi sẽ chỉ được sử dụng trong ngữ cảnh của một cuộc gọi phương thức và không có quyết toán thì tôi khuyên bạn nên sử dụng loại giá trị thay vì loại tham chiếu bằng ngôn ngữ làm cho sự khác biệt này. Ví dụ: trong C#, bạn có thể khai báo pháp nhân của mình dưới dạng struct thay vì class. Sau đó, các trường hợp ngắn hạn, phương thức cục bộ của bạn sẽ được cấp phát trên ngăn xếp chứ không phải là vùng heap, và chúng sẽ được giải thích một cách rõ ràng vào thời điểm mà phương thức trả về.

Và kỹ thuật này có thể tiến xa hơn ý tưởng ban đầu của bạn, bởi vì cấu trúc có thể được chuyển cho các phương pháp khác mà không cần phải lo lắng về việc phân tích tuổi thọ.

Đối với một mảng, như trong câu hỏi, bạn có thể sử dụng lệnh stackalloc để đạt được hiệu ứng này.

+0

Tôi đã nghĩ rằng mùi này tương tự đáng ngờ với các đối tượng được phân bổ so với đống heap :) –

2

"Hầu hết các lập trình viên đồng ý rằng thu gom rác là một điều tuyệt vời và trong hầu hết các ứng dụng cũng đáng giá trên đầu."

Nói chung quét ...

+0

Tôi thứ hai tuyên bố này. –

+0

Tôi đồng ý rằng việc thu gom rác thải là một điều tuyệt vời và trong hầu hết các ứng dụng đều đáng giá. ;-) – teedyay

+0

Đừng hiểu lầm, cả GC và non-GC đều có IMO. –

0

Điều này dường như chính xác cách D quản lý bộ nhớ. Rác của nó được thu thập trong khi cho phép bạn xóa cụ thể các đối tượng khi bạn muốn, hoặc thậm chí tránh được GC tất cả cùng nhau ủng hộ malloc/miễn phí. Các từ khóa scoped dường như làm những gì bạn muốn trên stack, mặc dù tôi không chắc chắn nếu nó thực sự phân bổ các đối tượng trên stack hoặc đống.

0

Mặc dù có thể có một số lợi ích khi kết hợp dọn dẹp thủ công với thu gom rác thải, có một lợi ích đáng kể để người thu gom rác tiếp tục quản lý đối tượng giữa thời gian họ không cần nữa và thời gian họ không thể hiển thị các tham chiếu bắt nguồn từ còn sống sót. Trong số những thứ khác, trong một hệ thống không phải GC, nó thường rất khó để chứng minh khi xóa một đối tượng mà không có cách nào có thể tham chiếu bất kỳ vẫn có thể tồn tại. Nếu một đối tượng bị xóa trong khi một tham chiếu vẫn tồn tại, một nỗ lực để sử dụng tham chiếu đó có thể gây ra hành vi không xác định tùy ý; bảo vệ chống lại nguy hiểm như vậy mà không có một GC nói chung là khó khăn. Ngược lại, nếu người ta vô hiệu hóa các đối tượng khi người ta thực hiện xong chúng, nhưng để lại việc sử dụng lại bộ nhớ đã chiếm dụng trước đó cho GC, người ta có thể đảm bảo rằng một nỗ lực sử dụng đối tượng bị vô hiệu sẽ thất bại theo kiểu có thể đoán trước được.

Ngẫu nhiên, nếu tôi thiết kế một "khung vi mô", tôi sẽ có các vùng heap riêng biệt cho các đối tượng có thể thay đổi và không thay đổi được. Bộ sưu tập rác sinh học hoạt động tốt nhất nếu mã có thể cho biết một đối tượng đã được viết vì gen0 hoặc gen1 đã được thu thập chưa. Để thực hiện một quyết tâm như vậy với các đối tượng có thể thay đổi được thì khó khăn hơn nhiều so với các đối tượng bất biến. Mặt khác, việc quản lý thủ công tuổi thọ hữu ích của các đối tượng có thể thay đổi thường dễ dàng hơn việc quản lý tuổi thọ hữu ích của các đối tượng bất biến, vì trước đây thường phải có một "chủ sở hữu" rõ ràng, trong khi thường không có chủ sở hữu.