Tại sao các ngôn ngữ như C và C ++ không có bộ sưu tập rác, trong khi Java thì có? [đóng cửa]


57

Chà, tôi biết rằng có những thứ như malloc / miễn phí cho C và new / use-a-killer để quản lý bộ nhớ trong C ++, nhưng tôi đã tự hỏi tại sao không có "cập nhật mới" cho các ngôn ngữ này cho phép người dùng có tùy chọn để quản lý bộ nhớ theo cách thủ công hoặc để hệ thống tự động thực hiện (bộ sưu tập rác) không?

Một phần của câu hỏi newb-ish, nhưng chỉ ở trong CS khoảng một năm.


9
Chúng tôi đã có một mô-đun phát triển iPhone trong học kỳ này. Sau khi ứng dụng mã hóa cho Android được 2 năm, câu hỏi này đánh vào hầu hết các lớp khá khó. Chỉ bây giờ chúng ta mới thấy Java thực sự đã cứu chúng ta bao nhiêu giờ khi không phải theo dõi các lỗi quản lý bộ nhớ khó chịu và không phải viết mã tấm nồi hơi.
siamii

7
@NullUserException, vì nó không chỉ định một cách để lấy lại bộ nhớ mà khá nhiều ngụ ý về một GC.
Winston Ewert

1
@ bizso09: Bạn đã xem ARC chưa? Không cần sử dụng GC chậm / béo / không xác định khi bạn đã hỗ trợ hệ thống để đếm tham chiếu: developer.apple.com/technology/ios5
JBRWilkinson

3
Các câu trả lời cho câu hỏi khá hay này đầy nhảm nhí tôn giáo.
abatishchev

1
Trong C và C ++, có thể lấy một con trỏ, chuyển nó thành int và thêm một số vào nó. Sau đó, trừ số từ int và đưa kết quả trở lại con trỏ. Bạn sẽ nhận được con trỏ giống như trước đây. Chúc may mắn trong việc thực hiện một GC mà không thu thập bộ nhớ đó trong khi địa chỉ của nó chỉ được lưu trữ trong biến cũng có giá trị khác. Tôi biết ví dụ này là ngớ ngẩn nhưng một danh sách liên kết XOR sử dụng một cái gì đó tương tự. Tôi sẽ đăng bài này như một câu trả lời nhưng câu hỏi đã được đóng lại.
Mary Spanik

Câu trả lời:


71

Thu gom rác yêu cầu cấu trúc dữ liệu để theo dõi phân bổ và / hoặc đếm tham chiếu. Chúng tạo ra chi phí bộ nhớ, hiệu suất và độ phức tạp của ngôn ngữ. C ++ được thiết kế để "gần với kim loại", nói cách khác, nó có mặt hiệu suất cao hơn của sự đánh đổi so với các tính năng tiện lợi. Các ngôn ngữ khác làm cho sự đánh đổi đó khác nhau. Đây là một trong những cân nhắc trong việc lựa chọn một ngôn ngữ, mà bạn thích nhấn mạnh.

Điều đó nói rằng, có rất nhiều lược đồ để đếm tham chiếu trong C ++ khá nhẹ và hiệu quả, nhưng chúng nằm trong các thư viện, cả thương mại và nguồn mở, chứ không phải là một phần của ngôn ngữ. Việc đếm tham chiếu để quản lý tuổi thọ đối tượng không giống như thu gom rác, nhưng nó giải quyết nhiều vấn đề tương tự và phù hợp hơn với cách tiếp cận cơ bản của C ++.


26
Một vấn đề thứ yếu là GC là không xác định. Đối tượng có thể vẫn còn hoặc không còn trong bộ nhớ sau khi chương trình đã "bỏ" nó. Vòng đời giới hạn là xác định, khi tham chiếu cuối cùng bị hủy, bộ nhớ bị hủy. Điều này có ý nghĩa không chỉ đối với hiệu quả bộ nhớ mà còn cả việc gỡ lỗi. Một lỗi lập trình phổ biến là đối tượng "zombie", bộ nhớ tham chiếu đã bị loại bỏ theo lý thuyết. GC có nhiều khả năng che dấu hiệu ứng này và tạo ra các lỗi không liên tục và cực kỳ khó theo dõi.
kylben

22
- gc hiện đại không theo dõi phân bổ hoặc đếm tài liệu tham khảo. Họ xây dựng một biểu đồ từ mọi thứ hiện có trên ngăn xếp và chỉ ngưng tụ và xóa sạch mọi thứ khác (đơn giản hóa), và thông thường, kết quả là làm giảm độ phức tạp của ngôn ngữ. Ngay cả lợi ích hiệu suất là nghi vấn.
Joel Coehoorn

13
Er, @kylben, toàn bộ vấn đề của việc tự động chuyển sang ngôn ngữ là không thể tham chiếu các đối tượng zombie, bởi vì GC chỉ giải phóng các đối tượng không thể tham chiếu! Bạn gặp phải loại lỗi khó theo dõi mà bạn đang nói đến khi bạn mắc lỗi với quản lý bộ nhớ thủ công.
Ben

14
-1, GC không tính tài liệu tham khảo. Ngoài ra, tùy thuộc vào việc sử dụng bộ nhớ và sơ đồ phân bổ của bạn, một GC có thể nhanh hơn (với chi phí sử dụng bộ nhớ). Vì vậy, tranh luận về hiệu suất là ngụy biện quá. Chỉ gần với kim loại là một điểm hợp lệ thực sự.
deadalnix

14
Cả Java và C # đều không sử dụng tính năng tham chiếu: Các lược đồ GC dựa trên đếm tham chiếu khá nguyên thủy so với và thực hiện kém hơn nhiều so với trình thu gom rác hiện đại (chủ yếu vì chúng cần ghi bộ nhớ để thay đổi số tham chiếu mỗi khi bạn sao chép tài liệu tham khảo!)
mikera

44

Nói đúng ra, không có quản lý bộ nhớ nào trong ngôn ngữ C. malloc () và free () không phải là từ khóa trong ngôn ngữ, mà chỉ là các hàm được gọi từ thư viện. Sự khác biệt này có thể là mô phạm bây giờ, bởi vì malloc () và free () là một phần của thư viện chuẩn C và sẽ được cung cấp bởi bất kỳ triển khai tuân thủ tiêu chuẩn nào của C, nhưng điều này không phải lúc nào cũng đúng.

Tại sao bạn muốn có một ngôn ngữ không có tiêu chuẩn để quản lý bộ nhớ? Điều này quay trở lại nguồn gốc của C là 'lắp ráp di động'. Có nhiều trường hợp phần cứng và thuật toán có thể được hưởng lợi từ, hoặc thậm chí yêu cầu, các kỹ thuật quản lý bộ nhớ chuyên dụng. Theo như tôi biết, không có cách nào để vô hiệu hóa hoàn toàn việc quản lý bộ nhớ riêng của Java và thay thế nó bằng chính bạn. Điều này chỉ đơn giản là không được chấp nhận trong một số tình huống hiệu suất cao / tài nguyên tối thiểu. C cung cấp sự linh hoạt gần như hoàn chỉnh để chọn chính xác cơ sở hạ tầng mà chương trình của bạn sẽ sử dụng. Cái giá phải trả là ngôn ngữ C cung cấp rất ít trợ giúp trong việc viết mã chính xác, không có lỗi.


2
+1 một cho câu trả lời tổng thể tốt, nhưng đặc biệt là "Giá phải trả là ngôn ngữ C cung cấp rất ít trợ giúp trong việc viết đúng, mã không có lỗi"
Shivan Dragon

2
C có quản lý bộ nhớ - nhưng nó chỉ hoạt động, vì vậy mọi người hầu như không nhận thấy nó. Có bộ nhớ tĩnh, thanh ghi và ngăn xếp. Cho đến khi bạn bắt đầu phân bổ ra khỏi đống, bạn vẫn ổn và bảnh bao. Đó là sự phân bổ đống mà làm rối tung mọi thứ. Đối với Java, mọi người đều có thể viết thời gian chạy Java của riêng mình - có rất nhiều lựa chọn, bao gồm cả cái có thể gọi là "Java của hệ thống". .NET có thể làm khá nhiều thứ mà C có thể - nó chỉ thua xa các khả năng riêng của C ++ (ví dụ: các lớp chỉ được quản lý trong .NET). Tất nhiên, bạn cũng có C ++. NET, có mọi thứ C ++ làm và mọi thứ .NET đều làm.
Luaan

1
@Luaan Tôi muốn nói rằng đó là một định nghĩa rất hào phóng về việc "quản lý bộ nhớ" "Cho đến khi bạn bắt đầu phân bổ ra khỏi đống, bạn vẫn ổn và bảnh bao. Đó là sự phân bổ đống làm rối tung mọi thứ", giống như nói là một chiếc xe hơi một chiếc máy bay hoàn toàn tốt, nó không thể bay được.
Charles E. Grant

1
@ CharlesE.Grant Vâng, một ngôn ngữ chức năng thuần túy có thể làm mọi thứ với kiểu quản lý bộ nhớ đó. Chỉ vì phân bổ heap là một sự đánh đổi tốt trong một số trường hợp sử dụng không có nghĩa đó là điểm chuẩn cho tất cả các ngôn ngữ / thời gian chạy. Nó không giống như quản lý bộ nhớ dừng lại là "quản lý bộ nhớ" chỉ vì nó đơn giản, đơn giản, ẩn đằng sau hậu trường. Thiết kế cấp phát bộ nhớ tĩnh vẫn là quản lý bộ nhớ, cũng như sử dụng tốt ngăn xếp và bất cứ thứ gì bạn có sẵn.
Luaan

"Bất kỳ triển khai tuân thủ tiêu chuẩn" là không đúng, chỉ dành cho thực hiện môi trường máy chủ tuân thủ tiêu chuẩn. Một số Nền tảng / Thư viện chuẩn, hầu hết cho Vi điều khiển nhúng 8 hoặc 16 bit, không cung cấp malloc()hoặc free(). (ví dụ: Trình biên dịch MLAP cho PIC)
12431234123412341234123

32

Câu trả lời thực sự là cách duy nhất để tạo ra một cơ chế thu gom rác an toàn, hiệu quả là có sự hỗ trợ ở cấp độ ngôn ngữ cho các tài liệu tham khảo mờ. (Hoặc, ngược lại, thiếu hỗ trợ ở cấp độ ngôn ngữ để thao tác bộ nhớ trực tiếp.)

Java và C # có thể làm điều đó bởi vì chúng có các kiểu tham chiếu đặc biệt không thể thao tác được. Điều này cho phép bộ thực thi tự do thực hiện những việc như di chuyển các đối tượng được phân bổ trong bộ nhớ , điều này rất quan trọng đối với việc thực hiện GC hiệu suất cao.

Đối với hồ sơ, không có triển khai GC hiện đại nào sử dụng tính tham chiếu , do đó hoàn toàn là cá trích đỏ. Các GC hiện đại sử dụng bộ sưu tập thế hệ, trong đó các phân bổ mới được xử lý về cơ bản giống như cách phân bổ ngăn xếp trong một ngôn ngữ như C ++, và sau đó định kỳ mọi đối tượng mới được phân bổ vẫn còn sống được chuyển đến một không gian "sống sót" riêng biệt và toàn bộ một thế hệ của các đối tượng được giải quyết cùng một lúc.

Cách tiếp cận này có những ưu và nhược điểm: nhược điểm là phân bổ heap trong ngôn ngữ hỗ trợ GC nhanh như phân bổ stack trong ngôn ngữ không hỗ trợ GC và nhược điểm là các đối tượng cần thực hiện dọn dẹp trước khi bị phá hủy yêu cầu một cơ chế riêng (ví dụ: usingtừ khóa của C # ) hoặc nếu không, mã dọn dẹp của họ chạy không xác định.

Lưu ý rằng một chìa khóa cho một GC hiệu suất cao là phải có hỗ trợ ngôn ngữ cho một lớp tài liệu tham khảo đặc biệt. C không có hỗ trợ ngôn ngữ này và sẽ không bao giờ; bởi vì C ++ có quá tải toán tử, nó có thể mô phỏng loại con trỏ của GC, mặc dù nó sẽ phải được thực hiện cẩn thận. Trên thực tế, khi Microsoft phát minh ra phương ngữ C ++ chạy theo CLR (thời gian chạy .NET), họ đã phải phát minh ra một cú pháp mới cho "tham chiếu kiểu C #" (ví dụ Foo^) để phân biệt với "tham chiếu kiểu C ++" (ví dụ Foo&).

Những gì C ++ có, và những gì được lập trình viên C ++ sử dụng thường xuyên, là những con trỏ thông minh , thực sự chỉ là một cơ chế đếm tham chiếu. Tôi sẽ không coi việc tham chiếu là GC "thật", nhưng nó mang lại nhiều lợi ích giống nhau, với chi phí hiệu năng chậm hơn so với quản lý bộ nhớ thủ công hoặc GC thực sự, nhưng với lợi thế là phá hủy xác định.

Vào cuối ngày, câu trả lời thực sự sôi nổi với một tính năng thiết kế ngôn ngữ. C đã đưa ra một lựa chọn, C ++ đã đưa ra một lựa chọn cho phép nó tương thích ngược với C trong khi vẫn cung cấp các lựa chọn thay thế đủ tốt cho hầu hết các mục đích và Java và C # đã đưa ra một lựa chọn khác không tương thích với C nhưng cũng đủ tốt cho hầu hết các mục đích. Thật không may, không có viên đạn bạc, nhưng làm quen với các lựa chọn khác nhau ngoài kia sẽ giúp bạn chọn đúng cho bất kỳ chương trình nào bạn đang cố gắng xây dựng.


4
Đây là câu trả lời thực sự cho câu hỏi
coredump

1
Đối với phần c ++, ngày nay bạn nên xem std :: unique_ptr và std :: move :)
Niclas Larsson

@NiclasLarsson: Tôi không chắc là tôi hiểu quan điểm của bạn. Bạn đang nói rằng đó std::unique_ptrlà "hỗ trợ cấp độ ngôn ngữ cho các tài liệu tham khảo mờ"? (Nó không phải là loại hỗ trợ tôi muốn nói, và tôi cũng không nghĩ rằng đó là đủ trừ khi hỗ trợ cho thao tác bộ nhớ trực tiếp cũng đã được gỡ bỏ từ C ++.) Tôi đề cập đến con trỏ thông minh trong câu trả lời của tôi, và tôi sẽ xem xét std:unique_ptrmột con trỏ thông minh , vì nó thực sự thực hiện đếm tham chiếu, nó chỉ hỗ trợ các trường hợp đặc biệt trong đó số lượng tham chiếu bằng 0 hoặc một (và std::movelà cơ chế cập nhật số tham chiếu).
Daniel Pryden

std::unique_ptrkhông có số tham chiếu và std::movekhông liên quan gì đến tài liệu tham khảo (vì vậy "không" hiệu suất đạt được). Mặc dù vậy, tôi thấy quan điểm của bạn, cũng như std::shared_ptrcó một số tham chiếu được cập nhật bởi std::move:)
Niclas Larsson

2
@ Mike76: Về phía phân bổ, bộ cấp phát GC sẽ hoạt động nhanh như phân bổ ngăn xếp và GC có thể phân bổ hàng ngàn đối tượng cùng một lúc. Bất kể bạn làm gì với việc thực hiện đếm lại, phân bổ và phân bổ sẽ không bao giờ nhanh hơn mallocfree. Vì vậy, có, một GC có thể nhanh hơn đáng kể. (Lưu ý rằng tôi đã nói "có thể" - tất nhiên hiệu suất chính xác của mỗi chương trình bị ảnh hưởng bởi nhiều yếu tố.)
Daniel Pryden

27

Bởi vì, khi sử dụng sức mạnh của C ++, không có nhu cầu.

Herb Sutter: " Tôi đã không xóa văn bản trong nhiều năm. "

xem Viết mã C ++ hiện đại: C ++ đã phát triển như thế nào trong những năm 21:10

Nó có thể gây ngạc nhiên cho nhiều lập trình viên C ++ có kinh nghiệm.


Hấp dẫn. Tài liệu đọc của tôi cho ngày hôm nay.
Surfasb

Bah, một video. Nhưng không bao giờ là ít, thú vị rồi.
Surfasb

2
video thú vị. 21 phút trong và 55 phút là các bit tốt nhất. Quá tệ, các cuộc gọi WinRT vẫn trông giống như C ++ / CLI.
gbjbaanb

2
@ dan04: Đó là sự thật. Nhưng sau đó, nếu bạn viết bằng C, bạn sẽ nhận được những gì bạn yêu cầu.
DeadMG

6
Quản lý con trỏ thông minh không đòi hỏi nhiều hơn là đảm bảo bạn không có các tài liệu tham khảo không cần thiết trong môi trường thu gom rác. Bởi vì GC không thể đọc được suy nghĩ của bạn, nó cũng không phải là phép thuật.
Tamás Szelei

15

"Tất cả" một trình thu gom rác là một quá trình chạy kiểm tra định kỳ để xem liệu có bất kỳ đối tượng không được ước tính nào trong bộ nhớ và nếu có xóa chúng. (Vâng, tôi biết đây là một sự đơn giản hóa quá mức). Đây không phải là một tài sản của ngôn ngữ, mà là khuôn khổ.

Có người thu gom rác bằng văn bản cho C và C ++ - điều này một ví dụ.

Một lý do tại sao một người chưa được "thêm" vào ngôn ngữ có thể là do khối lượng mã hiện tại sẽ không bao giờ sử dụng vì họ sử dụng mã riêng để quản lý bộ nhớ. Một lý do khác có thể là các loại ứng dụng được viết bằng C và C ++ không cần chi phí liên quan đến quy trình thu gom rác.


1
Nhưng các chương trình trong tương lai được viết sẽ bắt đầu sử dụng trình thu gom rác, phải không?
Templar tối

5
Mặc dù về mặt lý thuyết, bộ sưu tập rác không phụ thuộc vào bất kỳ ngôn ngữ lập trình nào, nhưng thật khó để viết một GC hữu ích cho C / C ++ và thậm chí không thể tạo ra một chứng minh ngu ngốc (ít nhất là dễ bị lừa như Java) - lý do Java có thể kéo nó tắt là vì nó chạy trong một môi trường ảo hóa được kiểm soát. Ngược lại, ngôn ngữ Java được thiết kế cho GC và bạn sẽ gặp khó khăn khi viết trình biên dịch Java không làm được GC.
tdammers

4
@tdammers: Tôi đồng ý rằng việc thu gom rác cần được ngôn ngữ hỗ trợ để có thể thực hiện được. Tuy nhiên, điểm chính không phải là ảo hóa và môi trường được kiểm soát, mà là gõ chính xác. C và C ++ được gõ yếu, vì vậy chúng cho phép những thứ như lưu trữ con trỏ trong biến số nguyên, tái tạo lại các con trỏ từ các offset và những thứ đó ngăn không cho người thu thập có thể nói một cách đáng tin cậy những gì có thể tiếp cận được (C ++ 11 cấm sau này cho phép ít nhất nhà sưu tập bảo thủ). Trong Java, bạn luôn biết thế nào là một tài liệu tham khảo, vì vậy bạn có thể thu thập nó một cách chính xác, ngay cả khi được biên dịch thành bản địa.
Jan Hudec

2
@ ThorbjørnRavnAndersen: Tôi có thể viết chương trình C hợp lệ lưu trữ các con trỏ theo cách mà không người thu gom rác nào có thể tìm thấy chúng. Nếu sau đó bạn móc một bộ thu gom rác vào mallocfree, bạn sẽ phá vỡ chương trình chính xác của tôi.
Ben Voigt

2
@ ThorbjørnRavnAndersen: Không, tôi sẽ không gọi freecho đến khi tôi hoàn thành nó. Nhưng trình thu gom rác được đề xuất của bạn không giải phóng bộ nhớ cho đến khi tôi gọi rõ ràng freekhông phải là trình thu gom rác.
Ben Voigt

12

C được thiết kế trong thời đại mà bộ sưu tập rác hầu như không phải là một lựa chọn. Nó cũng được thiết kế để sử dụng trong đó bộ sưu tập rác thường không hoạt động - kim loại trần, môi trường thời gian thực với bộ nhớ tối thiểu và hỗ trợ thời gian chạy tối thiểu. Hãy nhớ rằng C là ngôn ngữ thực hiện cho unix đầu tiên, chạy trên pdp-11 với bộ nhớ 64 * K *. C ++ ban đầu là một phần mở rộng của C - sự lựa chọn đã được thực hiện và rất khó để ghép bộ sưu tập rác vào một ngôn ngữ hiện có. Đó là loại thứ phải được xây dựng từ tầng trệt.


9

Tôi không có trích dẫn chính xác nhưng cả Bjarne và Herb Sutter đều nói điều gì đó dọc theo dòng:

C ++ không cần bộ thu gom rác, vì nó không có rác.

Trong C ++ hiện đại, bạn sử dụng con trỏ thông minh và do đó không có rác.


1
con trỏ thông minh là gì?
Templar tối

11
nếu nó đơn giản, không ai sẽ thực hiện bất kỳ GC nào.
deadalnix

7
@deadalnix: Phải, bởi vì không ai từng thực hiện bất cứ điều gì quá phức tạp, chậm chạp, cồng kềnh hoặc không cần thiết. Tất cả các phần mềm đều hiệu quả 100% mọi lúc, phải không?
Zach

5
@deadalnix - Cách tiếp cận quản lý bộ nhớ C ++ mới hơn so với trình thu gom rác. RAII được phát minh bởi Bjarne Stroustrup cho C ++. Dọn dẹp Destructor là một ý tưởng cũ hơn, nhưng các quy tắc để đảm bảo an toàn ngoại lệ là chìa khóa. Tôi không biết chính xác khi nào ý tưởng được mô tả lần đầu tiên nhưng tiêu chuẩn C ++ đầu tiên được hoàn thành vào năm 1998 và Stroustrups "Design and Evolution of C ++" không được công bố cho đến năm 1994 và các ngoại lệ là một bổ sung tương đối gần đây của C ++ - sau khi xuất bản "Hướng dẫn tham khảo C ++ được chú thích" năm 1990, tôi tin. GC được phát minh vào năm 1959 cho Lisp.
Steve314

1
@deadalnix - bạn có biết rằng ít nhất một máy ảo Java đã sử dụng một GC đếm tham chiếu có thể (gần như) được thực hiện bằng RAII kiểu C ++ bằng cách sử dụng một lớp con trỏ thông minh - chính xác là nó hiệu quả hơn đối với mã đa luồng so với các máy ảo hiện có? Xem www.research.ibm.com/people/d/dfb/ con / Bion01Concản.pdf. Một lý do bạn không thấy điều này trong C ++ trong thực tế là bộ sưu tập GC thông thường - nó có thể thu thập các chu kỳ, nhưng không thể chọn thứ tự phá hủy an toàn khi có chu kỳ và do đó không thể đảm bảo dọn dẹp hủy diệt đáng tin cậy.
Steve314

8

Bạn hỏi tại sao những ngôn ngữ này chưa được cập nhật để bao gồm một trình thu gom rác tùy chọn.

Vấn đề với bộ sưu tập rác tùy chọn là bạn không thể trộn mã sử dụng các mô hình khác nhau. Đó là, nếu tôi viết mã giả sử bạn đang sử dụng trình thu gom rác, bạn không thể sử dụng nó trong chương trình đã tắt bộ sưu tập rác. Nếu bạn làm thế, nó sẽ bị rò rỉ ở mọi nơi.


6

Bạn có thể tưởng tượng việc viết một trình xử lý thiết bị bằng ngôn ngữ với bộ sưu tập rác không? Có bao nhiêu bit có thể đi xuống dòng trong khi GC đang chạy?

Hay một hệ điều hành? Làm thế nào bạn có thể bắt đầu bộ sưu tập rác chạy trước khi bạn khởi động kernel?

C được thiết kế cho mức độ thấp gần với các tác vụ phần cứng. Vấn đề? Là một ngôn ngữ tốt đến nỗi nó cũng là một lựa chọn tốt cho nhiều nhiệm vụ cấp cao hơn. Các czars ngôn ngữ nhận thức được những sử dụng này nhưng họ cần hỗ trợ các yêu cầu của trình điều khiển thiết bị, mã nhúng và hệ điều hành là ưu tiên.


2
C tốt cho trình độ cao? Tôi khịt mũi uống hết bàn phím.
DeadMG

5
Chà, anh ta đã nói "nhiều nhiệm vụ cấp cao hơn". Anh ta có thể bị troll đếm (một, hai, nhiều ...). Và anh không thực sự nói cao hơn những gì. Mặc dù vậy, nói đùa là sự thật - bằng chứng là nhiều dự án cấp cao quan trọng đã được phát triển thành công ở C. Có thể có nhiều lựa chọn tốt hơn cho nhiều dự án đó, nhưng một dự án hoạt động là bằng chứng mạnh mẽ hơn suy đoán về những gì có thể đã được.
Steve314

Có một số hệ điều hành được quản lý và chúng hoạt động khá tốt. Trong thực tế, khi bạn quản lý toàn bộ hệ thống, hiệu suất đạt được từ việc sử dụng mã được quản lý thậm chí còn thấp hơn, nhanh hơn so với mã không được quản lý trong các tình huống thực tế. Tất nhiên, tất cả đều là "HĐH nghiên cứu" - gần như không có cách nào để làm cho chúng tương thích với mã không được quản lý hiện có bên cạnh việc tạo ra một HĐH không được quản lý ảo hóa hoàn toàn trong HĐH được quản lý. Microsoft đã đề nghị tại một số điểm rằng họ có thể thay thế Windows Server bằng một trong số đó, vì ngày càng nhiều mã máy chủ được viết trên .NET.
Luaan

6

Câu trả lời ngắn gọn và nhàm chán cho câu hỏi này là cần phải có một ngôn ngữ không được thu gom rác ngoài kia cho những người viết công cụ thu gom rác. Về mặt khái niệm không dễ để có một ngôn ngữ đồng thời cho phép kiểm soát rất chính xác bố cục bộ nhớ có một GC chạy trên đầu.

Câu hỏi khác là tại sao C và C ++ không có người thu gom rác. Chà, tôi biết C ++ có một vài người trong số họ nhưng họ không thực sự nổi tiếng vì họ buộc phải đối phó với một ngôn ngữ không được thiết kế để trở thành GC-ed ngay từ đầu và những người vẫn sử dụng C ++ trong tuổi này không thực sự là loại bỏ lỡ một GC.

Ngoài ra, thay vì thêm GC vào ngôn ngữ không phải là ngôn ngữ cũ, việc tạo một ngôn ngữ mới có hầu hết các cú pháp tương tự trong khi hỗ trợ một ngôn ngữ thực sự dễ dàng hơn. Java và C # là những ví dụ tốt về điều này.


1
Ở đâu đó trên lập trình viên. SO hoặc SO, có một tuyên bố ai đó đã nói với tôi rằng ai đó đang làm việc với một thứ tự thu gom rác tự động - IIRC về cơ bản triển khai VM bằng ngôn ngữ GC, với một tập hợp con bootstrapping được sử dụng để thực hiện chính GC. Tôi quên tên. Khi tôi nhìn vào nó, hóa ra về cơ bản họ sẽ không bao giờ đạt được bước nhảy vọt từ cấp độ con mà không có GC đến cấp độ làm việc. Đây khả năng về nguyên tắc, nhưng AFAIK nó chưa bao giờ đạt được trong thực tế - nó chắc chắn là một trường hợp làm việc một cách khó khăn.
Steve314

@ Steve314: Tôi rất muốn thấy điều đó nếu bạn nhớ nơi bạn tìm thấy nó!
hugomg

Tìm thấy rồi! Xem các bình luận để stackoverflow.com/questions/3317329/ đề cập đến Klein VM. Một phần của vấn đề tìm kiếm nó - câu hỏi đã bị đóng.
Steve314

BTW - Tôi dường như không thể bắt đầu nhận xét của mình với @missingno - những gì mang lại?
Steve314

@ steve314: Đã đính kèm câu trả lời mà chủ đề này được đính kèm, tôi đã nhận được thông báo cho tất cả các bình luận. Làm một @ -post trong trường hợp này sẽ là dư thừa và không được SE cho phép ( mặc dù vậy đừng hỏi tôi tại sao ). (Tuy nhiên, nguyên nhân thực sự là do số của tôi bị mất)
hugomg

5

Có nhiều vấn đề khác nhau, bao gồm ...

  • Mặc dù GC được phát minh trước C ++ và có thể trước C, cả C và C ++ đều được triển khai trước khi các GC được chấp nhận rộng rãi như là thực tế.
  • Bạn không thể dễ dàng thực hiện một ngôn ngữ và nền tảng GC mà không có ngôn ngữ không phải là GC cơ bản.
  • Mặc dù GC hiệu quả hơn nhiều so với không phải là GC đối với mã ứng dụng điển hình được phát triển trong thời gian điển hình, v.v., có những vấn đề mà nỗ lực phát triển nhiều hơn là sự đánh đổi tốt và quản lý bộ nhớ chuyên biệt sẽ vượt trội so với GC có mục đích chung. Bên cạnh đó, C ++ thường hiệu quả hơn nhiều so với hầu hết các ngôn ngữ GC ngay cả khi không có bất kỳ nỗ lực phát triển bổ sung nào.
  • GC không an toàn hơn so với RAII kiểu C ++. RAII cho phép các tài nguyên khác ngoài bộ nhớ được tự động dọn sạch, về cơ bản vì nó hỗ trợ các công cụ phá hủy đáng tin cậy và kịp thời. Chúng không thể được kết hợp với các phương pháp GC thông thường vì các vấn đề với chu kỳ tham chiếu.
  • Các ngôn ngữ GC có các loại rò rỉ bộ nhớ đặc trưng của riêng chúng, đặc biệt liên quan đến bộ nhớ sẽ không bao giờ được sử dụng lại, nhưng ở đó các tham chiếu hiện có tồn tại chưa bao giờ bị loại bỏ hoặc ghi đè. Sự cần thiết phải làm điều này một cách rõ ràng về nguyên tắc không khác gì so với nhu cầu deletehoặc freerõ ràng. Cách tiếp cận GC vẫn có một lợi thế - không có tham chiếu lơ lửng - và phân tích tĩnh có thể bắt được một số trường hợp, nhưng một lần nữa, không có một giải pháp hoàn hảo nào cho tất cả các trường hợp.

Về cơ bản, một phần là về tuổi của các ngôn ngữ, nhưng sẽ luôn có một nơi dành cho các ngôn ngữ không phải là GC - ngay cả khi đó là một chút của một nơi không có gì đó. Và nghiêm túc, trong C ++, việc thiếu GC không phải là vấn đề lớn - bộ nhớ của bạn được quản lý khác nhau, nhưng nó không được quản lý.

Microsofts được quản lý C ++ có ít nhất một số khả năng kết hợp giữa GC và không phải GC trong cùng một ứng dụng, cho phép kết hợp các lợi thế của từng loại, nhưng tôi không có kinh nghiệm để nói rằng điều này hoạt động tốt như thế nào trong thực tế.

Liên kết lại liên kết đến câu trả lời liên quan của tôi ...


4

Bộ sưu tập rác về cơ bản không tương thích với ngôn ngữ hệ thống được sử dụng để phát triển trình điều khiển cho phần cứng có khả năng DMA.

Hoàn toàn có khả năng con trỏ duy nhất đến một đối tượng sẽ được lưu trữ trong một thanh ghi phần cứng ở một số thiết bị ngoại vi. Vì người thu gom rác sẽ không biết về điều này, nên nó sẽ nghĩ rằng đối tượng không thể truy cập được và thu thập nó.

Đối số này giữ gấp đôi để nén GC. Ngay cả khi bạn cẩn thận duy trì các tham chiếu trong bộ nhớ đến các đối tượng được sử dụng bởi các thiết bị ngoại vi phần cứng, khi GC di chuyển đối tượng, nó sẽ không biết cách cập nhật con trỏ có trong thanh ghi cấu hình ngoại vi.

Vì vậy, bây giờ bạn cần một hỗn hợp bộ đệm DMA bất động và các đối tượng được quản lý bởi GC, có nghĩa là bạn có tất cả các nhược điểm của cả hai.


Có thể cho rằng tất cả các nhược điểm của cả hai, nhưng ít trường hợp của mỗi nhược điểm, và giống nhau cho các lợi thế. Rõ ràng có sự phức tạp trong việc xử lý nhiều loại bộ nhớ hơn, nhưng cũng có thể tránh được sự phức tạp bằng cách chọn đúng con ngựa cho mỗi khóa học trong mã của bạn. Không thể, tôi tưởng tượng, nhưng có một lỗ hổng lý thuyết ở đó. Tôi đã suy đoán về việc trộn lẫn GC và không phải GC trong cùng một ngôn ngữ trước đây, nhưng không phải cho trình điều khiển thiết bị - nhiều hơn để có ứng dụng chủ yếu là GC, nhưng với một số thư viện cấu trúc dữ liệu cấp thấp được quản lý bộ nhớ thủ công.
Steve314

@ Steve314: Bạn sẽ không nói rằng việc nhớ những đối tượng nào cần được giải phóng bằng tay cũng có hại như việc nhớ giải phóng mọi thứ? . Vì vậy, rất nhiều phức tạp thêm cho không có gì.
Ben Voigt

2
Không phải nếu có sự phân chia rõ ràng giữa mã cấp cao là tất cả GC và mã cấp thấp không phù hợp với GC. Tôi chủ yếu phát triển ý tưởng trong khi xem xét D vài năm trước, cho phép bạn từ chối sử dụng GC nhưng không cho phép bạn chọn tham gia lại. Ví dụ như thư viện cây B +. Toàn bộ vùng chứa phải là GC, nhưng các nút cấu trúc dữ liệu có thể không - thực hiện quét tùy chỉnh qua các nút lá hiệu quả hơn là làm cho GC thực hiện tìm kiếm đệ quy thông qua các nút nhánh. Tuy nhiên, quá trình quét đó cần phải báo cáo các mục có trong GC.
Steve314

Vấn đề là, đó là một phần chức năng. Đối xử với các nút cây B + như quản lý bộ nhớ WRT đặc biệt không khác gì coi chúng là WRT đặc biệt các nút cây B +. Đó là một thư viện được đóng gói và mã ứng dụng không cần biết rằng hành vi của GC đã bị bỏ qua / đặc biệt. Ngoại trừ điều đó, ít nhất là vào thời điểm đó, điều đó là không thể đối với D - như tôi đã nói, không có cách nào để chọn lại và báo cáo các mục có trong GC là gốc GC tiềm năng.
Steve314

3

Bởi vì, C & C ++ là các ngôn ngữ cấp độ tương đối thấp dành cho mục đích chung, thậm chí, ví dụ, để chạy trên bộ xử lý 16 bit với 1 MB bộ nhớ trong một hệ thống nhúng, không thể lãng phí bộ nhớ với gc.


1
"Hệ thống nhúng"? Tại thời điểm C được chuẩn hóa (1989), nó cần có khả năng xử lý các PC có bộ nhớ 1 MB.
dan04

Tôi đồng ý, tôi đã trích dẫn một ví dụ hiện tại.
Petruza

1 MB ??? Holy schmoley, ai sẽ cần nhiều RAM như vậy? </ billGates>
Mark K Cowan

2

Có các trình thu gom rác trong C ++ và C. Không chắc cách thức hoạt động trong C, nhưng trong C ++, bạn có thể tận dụng RTTI để tự động khám phá biểu đồ đối tượng của mình và sử dụng biểu đồ đó để thu gom rác.

Theo hiểu biết của tôi, bạn không thể viết Java mà không có trình thu gom rác. Một tìm kiếm nhỏ bật lên này .

Sự khác biệt chính giữa Java và C / C ++ là trong C / C ++, sự lựa chọn luôn là của bạn, trong khi ở Java, bạn thường không có các tùy chọn theo thiết kế.


Và cũng là những người thu gom rác chuyên dụng được triển khai tốt hơn, hiệu quả hơn và phù hợp hơn với ngôn ngữ. :)
Tối đa

Không, bạn không thể sử dụng RTTI để tự động khám phá biểu đồ đối tượng trong C / C ++: Đó là các đối tượng dữ liệu cũ đơn giản làm hỏng mọi thứ. Đơn giản là không có thông tin RTTI được lưu trữ trong một đối tượng dữ liệu cũ đơn giản sẽ cho phép trình thu gom rác phân biệt giữa các con trỏ và các con trỏ trong đối tượng đó. Thậm chí tệ hơn, các con trỏ không cần phải được căn chỉnh hoàn hảo trên tất cả các phần cứng, do đó, với một đối tượng 16 byte, có 9 vị trí có thể lưu trữ một con trỏ 64 bit, chỉ có hai vị trí không trùng nhau.
cmaster

2

Đó là một sự đánh đổi giữa hiệu suất và an toàn.

Không có gì đảm bảo rằng rác của bạn sẽ được thu thập trong Java, do đó, nó có thể bị treo xung quanh khi sử dụng hết dung lượng trong một thời gian dài, trong khi việc quét các đối tượng không được ước tính (ví dụ như rác) cũng mất nhiều thời gian hơn so với việc xóa hoặc giải phóng rõ ràng một đối tượng không sử dụng.

Tất nhiên, ưu điểm là người ta có thể xây dựng một ngôn ngữ mà không có con trỏ hoặc không bị rò rỉ bộ nhớ, do đó, một người có nhiều khả năng tạo ra mã chính xác.

Đôi khi có thể có một chút "tôn giáo" đối với những cuộc tranh luận này - được cảnh báo!


1

Dưới đây là danh sách các vấn đề cố hữu của GC, khiến nó không sử dụng được trong ngôn ngữ hệ thống như C:

  • GC phải chạy dưới mức mã mà các đối tượng mà nó quản lý. Đơn giản là không có mức như vậy trong kernel.

  • Thỉnh thoảng, một GC phải dừng mã được quản lý. Bây giờ hãy nghĩ về những gì sẽ xảy ra nếu nó làm điều đó với kernel của bạn. Tất cả quá trình xử lý trên máy của bạn sẽ dừng lại trong khoảng một phần nghìn giây trong khi GC quét tất cả các cấp phát bộ nhớ hiện có. Điều này sẽ giết chết mọi nỗ lực để tạo ra các hệ thống hoạt động theo các yêu cầu nghiêm ngặt theo thời gian thực.

  • Một GC cần có khả năng phân biệt giữa con trỏ và con trỏ. Đó là, nó phải có khả năng nhìn vào mọi đối tượng bộ nhớ tồn tại và có thể tạo ra một danh sách các điểm bù nơi con trỏ của nó có thể được tìm thấy.

    Khám phá này phải hoàn hảo: GC phải có khả năng đuổi theo tất cả các con trỏ mà nó khám phá. Nếu nó được xác định là dương tính giả, nó có thể sẽ sụp đổ. Nếu không phát hiện ra âm tính giả, nó có thể sẽ phá hủy một đối tượng vẫn đang sử dụng, làm hỏng mã được quản lý hoặc âm thầm làm hỏng dữ liệu của nó.

    Điều này hoàn toàn đòi hỏi thông tin loại được lưu trữ trong mọi đối tượng tồn tại. Tuy nhiên, cả C và C ++ đều cho phép các đối tượng dữ liệu cũ đơn giản không chứa thông tin loại.

  • GC là một doanh nghiệp vốn đã chậm. Các lập trình viên đã được xã hội hóa với Java có thể không nhận ra điều này, nhưng các chương trình có thể là các đơn đặt hàng có cường độ nhanh hơn khi chúng không được triển khai trong Java. Và một trong những yếu tố khiến Java chậm là GC. Đây là những gì ngăn cản các ngôn ngữ được phân loại như Java được sử dụng trong siêu máy tính. Nếu máy của bạn tiêu tốn một triệu mỗi năm tiền điện, bạn không muốn trả thậm chí 10% số tiền đó cho việc thu gom rác.

C và C ++ là các ngôn ngữ được tạo để hỗ trợ tất cả các trường hợp sử dụng có thể. Và, như bạn thấy, nhiều trường hợp sử dụng bị loại trừ bởi bộ sưu tập rác. Vì vậy, để hỗ trợ các trường hợp sử dụng này, C / C ++ không thể được thu gom rác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.