Làm thế nào để thu gom rác hoạt động trong các ngôn ngữ được biên dịch nguyên bản?


79

Sau khi duyệt một số câu trả lời Stack Overflow, rõ ràng một số ngôn ngữ được biên dịch nguyên bản có bộ sưu tập rác . Nhưng tôi không rõ chính xác nó sẽ hoạt động như thế nào.

Tôi hiểu làm thế nào bộ sưu tập rác có thể làm việc với một ngôn ngữ diễn giải. Trình thu gom rác đơn giản sẽ chạy cùng với trình thông dịch và xóa các đối tượng không sử dụng và không thể truy cập khỏi bộ nhớ của chương trình. Cả hai đang chạy cùng nhau.

Làm thế nào điều này sẽ làm việc với các ngôn ngữ biên dịch mặc dù? Tôi hiểu rằng một khi trình biên dịch đã biên dịch mã nguồn thành mã đích - cụ thể là mã máy gốc - nó đã được thực hiện. Công việc của nó đã kết thúc. Vì vậy, làm thế nào chương trình biên dịch có thể được thu thập rác?

Trình biên dịch có hoạt động với CPU theo một cách nào đó trong khi chương trình được thực thi để xóa các đối tượng "rác" không? Hoặc trình biên dịch có bao gồm một số trình thu gom rác tối thiểu trong tệp thực thi của chương trình đã biên dịch.

Tôi tin rằng tuyên bố sau của tôi sẽ có giá trị hơn so với tuyên bố trước do đoạn trích này từ câu trả lời này trên Stack Overflow :

Một ngôn ngữ lập trình như vậy là Eiffel. Hầu hết các trình biên dịch Eiffel tạo mã C vì lý do tính di động. Mã C này được sử dụng để sản xuất mã máy bằng trình biên dịch C tiêu chuẩn. Việc triển khai Eiffel cung cấp GC (và đôi khi thậm chí là chính xác) cho mã được biên dịch này và không cần VM. Cụ thể, trình biên dịch VisualEiffel đã tạo mã máy x86 riêng trực tiếp với sự hỗ trợ đầy đủ của GC .

Câu lệnh cuối cùng dường như ngụ ý rằng trình biên dịch bao gồm một số chương trình trong tệp thực thi cuối cùng hoạt động như một trình thu gom rác trong khi chương trình đang chạy.

Trang trên trang web của ngôn ngữ D về bộ sưu tập rác - được biên soạn nguyên bản và có trình thu gom rác tùy chọn - dường như cũng gợi ý rằng một số chương trình nền chạy cùng với chương trình thực thi ban đầu để thực hiện thu gom rác.

D là ngôn ngữ lập trình hệ thống có hỗ trợ thu gom rác. Thông thường không cần thiết phải giải phóng bộ nhớ rõ ràng. Chỉ cần phân bổ khi cần và trình thu gom rác sẽ định kỳ trả lại tất cả bộ nhớ chưa sử dụng cho nhóm bộ nhớ khả dụng.

Nếu phương pháp được đề cập ở trên được sử dụng, chính xác thì nó sẽ hoạt động như thế nào? Trình biên dịch có lưu một bản sao của một số chương trình thu gom rác và dán nó vào mỗi tệp thực thi mà nó tạo ra không?

Hay tôi là thiếu sót trong suy nghĩ của tôi? Nếu vậy, phương pháp nào được sử dụng để thực hiện thu gom rác cho các ngôn ngữ được biên dịch và chính xác chúng sẽ hoạt động như thế nào?


1
Tôi sẽ đánh giá cao nếu cử tri thân thiết của câu hỏi này có thể nêu chính xác những gì sai để tôi có thể sửa chữa nó?
Christian Dean

6
Nếu bạn chấp nhận thực tế rằng GC về cơ bản là một phần của thư viện được yêu cầu bởi việc triển khai ngôn ngữ lập trình cụ thể, thì ý chính của câu hỏi của bạn không liên quan gì đến GC per se và mọi thứ phải làm với liên kết tĩnh so với động .
Theodoros Chatzigiannakis

7
Bạn có thể coi trình thu gom rác là một phần của thư viện thời gian chạy thực hiện ngôn ngữ tương đương với malloc().
Barmar

9
Hoạt động của bộ thu gom rác phụ thuộc vào đặc tính của bộ cấp phát chứ không phải mô hình biên dịch . Người cấp phát biết mọi đối tượng đã được phân bổ; nó phân bổ chúng. Bây giờ tất cả những gì bạn cần là một số cách để biết những đối tượng nào vẫn còn sống và người sưu tầm có thể giải quyết tất cả các đối tượng ngoại trừ chúng. Không có gì trong mô tả đó có liên quan đến mô hình biên dịch.
Eric Lippert

1
GC là một tính năng của bộ nhớ động, không phải là một tính năng của trình thông dịch.
Dmitry Grigoryev

Câu trả lời:


52

Bộ sưu tập rác trong một ngôn ngữ được biên dịch hoạt động giống như trong một ngôn ngữ được giải thích. Các ngôn ngữ như Go sử dụng theo dõi bộ thu gom rác mặc dù mã của chúng thường được biên dịch theo mã máy trước thời hạn.

(Truy tìm) bộ sưu tập rác thường bắt đầu bằng cách đi bộ ngăn xếp cuộc gọi của tất cả các luồng hiện đang chạy. Các đối tượng trên các ngăn xếp luôn luôn sống. Sau đó, trình thu gom rác đi qua tất cả các đối tượng được chỉ ra bởi các đối tượng sống, cho đến khi toàn bộ biểu đồ đối tượng sống được phát hiện.

Rõ ràng là làm điều này đòi hỏi thêm thông tin mà các ngôn ngữ như C không cung cấp. Cụ thể, nó yêu cầu một bản đồ của khung ngăn xếp của từng hàm chứa phần bù của tất cả các con trỏ (và có thể là kiểu dữ liệu của chúng) cũng như bản đồ của tất cả các bố cục đối tượng có cùng thông tin.

Tuy nhiên, thật dễ dàng để thấy rằng các ngôn ngữ có bảo đảm loại mạnh (ví dụ: nếu các con trỏ chuyển sang các kiểu dữ liệu khác nhau không được phép) thực sự có thể tính toán các bản đồ đó trong thời gian biên dịch. Họ chỉ đơn giản là lưu trữ một liên kết giữa các địa chỉ lệnh và các bản đồ khung ngăn xếp và một liên kết giữa các kiểu dữ liệu và bản đồ bố trí đối tượng bên trong nhị phân. Thông tin này sau đó cho phép họ thực hiện duyệt đồ thị đối tượng.

Bản thân trình thu gom rác không gì khác hơn là một thư viện được liên kết với chương trình, tương tự như thư viện chuẩn C. Ví dụ, thư viện này có thể cung cấp một hàm tương tự như malloc()chạy thuật toán thu thập nếu áp suất bộ nhớ cao.


9
Giữa các thư viện tiện ích và biên dịch JIT, các dòng giữa "được biên dịch thành gốc" và "chạy trong môi trường thời gian chạy" ngày càng trở nên mờ nhạt.
corsiKa

6
Chỉ cần thêm một chút về các ngôn ngữ không hỗ trợ GC: Đúng là C và các ngôn ngữ khác không cung cấp thông tin về ngăn xếp cuộc gọi, nhưng nếu bạn ổn với một số mã dành riêng cho nền tảng (thường bao gồm một chút mã lắp ráp) vẫn có thể thực hiện "thu gom rác bảo thủ". Các Boehm GC là một ví dụ của việc này được sử dụng trong các chương trình thực tế đời sống.
Matti Virkkunen

2
@corsiKa Hay nói đúng hơn, dòng này khác biệt hơn nhiều. Bây giờ chúng ta thấy rằng đó là những khái niệm không liên quan khác nhau, và không phải là từ trái nghĩa của nhau.
Kroltan

4
Một sự phức tạp bổ sung mà bạn cần lưu ý trong thời gian chạy được biên dịch so với thời gian diễn giải liên quan đến câu này trong câu trả lời của bạn: "(Truy tìm) bộ sưu tập rác thường bắt đầu bằng cách đi bộ ngăn xếp cuộc gọi của tất cả các luồng hiện đang chạy." Kinh nghiệm của tôi về việc thực hiện GC trong một môi trường được biên dịch là theo dõi các ngăn xếp là không đủ. Điểm bắt đầu thường tạm dừng các luồng đủ lâu để theo dõi từ các thanh ghi của chúng , bởi vì chúng có thể có các tham chiếu trong các thanh ghi chưa được lưu trữ trong ngăn xếp. Đối với một thông dịch viên, điều này thường không ...
Jules

... Một vấn đề, bởi vì môi trường có thể sắp xếp cho GC diễn ra tại "điểm an toàn" nơi người phiên dịch biết rằng tất cả dữ liệu được lưu trữ an toàn trong ngăn xếp được giải thích.
Jules

123

Trình biên dịch có lưu một bản sao của một số chương trình thu gom rác và dán nó vào mỗi tệp thực thi mà nó tạo ra không?

Nghe có vẻ không hợp lý và kỳ lạ, nhưng có. Trình biên dịch có toàn bộ thư viện tiện ích, chứa nhiều hơn cả mã bộ sưu tập rác và các lệnh gọi đến thư viện này sẽ được chèn vào mỗi tệp thực thi mà nó tạo ra. Đây được gọi là thư viện thời gian chạy và bạn sẽ ngạc nhiên về số lượng nhiệm vụ khác nhau mà nó thường phục vụ.


51
@ChristianDean Lưu ý rằng ngay cả C cũng có thư viện thời gian chạy. Mặc dù nó không có GC, nhưng nó vẫn thực hiện quản lý bộ nhớ thông qua thư viện thời gian chạy đó: malloc()free()không được tích hợp vào ngôn ngữ, không phải là một phần của hệ điều hành, nhưng là các chức năng trong thư viện này. C ++ đôi khi cũng được biên dịch với thư viện bộ sưu tập rác, mặc dù ngôn ngữ không được thiết kế với GC.
amon

18
C ++ cũng chứa một thư viện thời gian chạy thực hiện những việc như make dynamic_castvà ngoại lệ, ngay cả khi bạn không thêm một GC.
Sebastian Redl

23
Thư viện thời gian chạy không nhất thiết phải được sao chép vào mỗi tệp thực thi (được gọi là liên kết tĩnh), nó chỉ có thể được tham chiếu (đường dẫn đến tệp nhị phân chứa thư viện) và được truy cập tại thời điểm thực hiện: đây là liên kết động.
mouviciel

16
Trình biên dịch cũng không bắt buộc phải nhảy trực tiếp vào điểm vào chương trình của bạn mà không có điều gì khác xảy ra. Tôi đang đưa ra một phỏng đoán có giáo dục rằng mọi trình biên dịch thực sự chèn một loạt mã khởi tạo dành riêng cho nền tảng trước khi nó gọi main(), và nó hoàn toàn hợp pháp để nói, kích hoạt một luồng GC trong mã này. . nếu GC di dời các đối tượng.
millimoose

15
@ cối xay: Có. Ví dụ, trên GCC, đoạn mã này là crt0.o(viết tắt của " C R un T ime, những điều cơ bản"), được liên kết với mọi chương trình (hoặc ít nhất là mọi chương trình không hoạt động ).
Jörg W Mittag

58

Hoặc trình biên dịch có bao gồm một số trình thu gom rác tối thiểu trong mã của chương trình được biên dịch.

Đó là một cách kỳ quặc để nói rằng trình biên dịch liên kết chương trình với một thư viện thực hiện bộ sưu tập rác. Nhưng vâng, đó là những gì đang xảy ra.

Điều này không có gì đặc biệt: trình biên dịch thường liên kết hàng tấn thư viện vào các chương trình họ biên dịch; mặt khác, các chương trình được biên dịch không thể làm được gì nhiều mà không thực hiện lại nhiều thứ từ đầu: thậm chí viết văn bản lên màn hình / một tệp / cảm biến yêu cầu một thư viện.

Nhưng có lẽ GC khác với các thư viện khác, nơi cung cấp các API rõ ràng mà người dùng gọi?

Không: trong hầu hết các ngôn ngữ, các thư viện thời gian chạy thực hiện rất nhiều công việc hậu trường mà không cần API công khai, ngoài GC. Hãy xem xét ba ví dụ sau:

  1. Tuyên truyền ngoại lệ và ngăn xếp gọi / hủy bỏ cuộc gọi.
  2. Cấp phát bộ nhớ động (thường không chỉ gọi một hàm, như trong C, ngay cả khi không có bộ sưu tập rác).
  3. Theo dõi thông tin loại động (đối với phôi vv).

Vì vậy, một thư viện thu gom rác hoàn toàn không có gì đặc biệt, và một tiên nghiệm không liên quan gì đến việc liệu một chương trình có được biên soạn trước thời hạn hay không.


điều này dường như không cung cấp bất cứ điều gì đáng kể qua các điểm được thực hiện và giải thích trong câu trả lời hàng đầu được đăng 3 giờ trước
gnat

11
@gnat Tôi cảm thấy nó hữu ích / cần thiết bởi vì câu trả lời hàng đầu không đủ mạnh cho đến nay: nó đề cập đến các sự kiện tương tự, nhưng nó không chỉ ra rằng việc thu gom rác là một sự phân biệt hoàn toàn giả tạo. Về cơ bản, giả định của OP là thiếu sót, và câu trả lời hàng đầu không đề cập đến điều này. Của tôi làm điều đó (trong khi tránh các thuật ngữ khá thô tục là thiếu sót).
Konrad Rudolph

Nó không đặc biệt lắm, nhưng tôi nói nó hơi đặc biệt, vì mọi người thường nghĩ về các thư viện như một cái gì đó mà họ gọi một cách rõ ràng từ mã của họ; thay vì thực hiện các ngữ nghĩa cơ bản của ngôn ngữ. Tôi nghĩ rằng giả định sai của OP ở đây là một trình biên dịch chỉ để dịch mã theo cách đơn giản hơn hoặc ít hơn, chứ không phải là công cụ với các cuộc gọi thư viện mà tác giả không chỉ định.
millimoose

7
@millimoose Các thư viện thời gian chạy hoạt động đằng sau hậu trường theo nhiều cách mà không có sự tương tác rõ ràng của người dùng. Xem xét việc truyền ngoại lệ và ngăn xếp gọi / hủy bỏ cuộc gọi. Xem xét cấp phát bộ nhớ động (thường không chỉ gọi một hàm, như trong C, ngay cả khi không có bộ sưu tập rác). Xem xét xử lý thông tin loại động (đối với phôi, v.v.). Vì vậy, GC thực sự không phải là duy nhất.
Konrad Rudolph

3
Vâng, tôi thừa nhận tôi đã nói từ đó một cách kỳ lạ. Điều đó đơn giản là vì tôi đã hoài nghi về trình biên dịch thực tế đang làm một cái gì đó như thế. Nhưng bây giờ tôi nghĩ về nó, nó thực sự có ý nghĩa hơn nhiều. Trình biên dịch có thể đơn giản liên kết một trình thu gom rác như bất kỳ phần nào khác của thư viện chuẩn. Tôi tin rằng một số nhầm lẫn của tôi xuất phát từ suy nghĩ của một người thu gom rác chỉ là một phần của việc thực hiện thông dịch viên chứ không phải là một chương trình riêng biệt theo đúng nghĩa của nó.
Christian Dean

23

Làm thế nào điều này sẽ làm việc với các ngôn ngữ biên dịch mặc dù?

Từ ngữ của bạn là sai. Một ngôn ngữ lập trình là một đặc điểm kỹ thuật bằng văn bản trong một số báo cáo kỹ thuật (đối với một ví dụ tốt, xem R5RS ). Trên thực tế, bạn đang đề cập đến một số thực hiện ngôn ngữ cụ thể (đó là một phần mềm).

(một số ngôn ngữ lập trình có thông số kỹ thuật xấu hoặc thậm chí thiếu ngôn ngữ hoặc chỉ tuân thủ một số triển khai mẫu; ngôn ngữ lập trình xác định hành vi - ví dụ: nó có cú phápngữ nghĩa -, nó không phải là sản phẩm phần mềm, nhưng có thể là được triển khai bởi một số sản phẩm phần mềm, nhiều ngôn ngữ lập trình có một số triển khai, đặc biệt, "được biên dịch" là một tính từ áp dụng cho việc triển khai - ngay cả khi một số ngôn ngữ lập trình được trình thông dịch thực hiện dễ dàng hơn so với trình biên dịch.)

Tôi hiểu rằng một khi trình biên dịch đã biên dịch mã nguồn thành mã đích - cụ thể là mã máy gốc - nó đã được thực hiện. Công việc của nó đã kết thúc.

Lưu ý rằng trình thông dịch và trình biên dịch có nghĩa lỏng lẻo và một số triển khai ngôn ngữ có thể được coi là cả hai. Nói cách khác, có một sự liên tục ở giữa. Đọc Sách Rồng mới nhất và suy nghĩ về mã byte , biên dịch JIT , mã động phát ra mã C được biên dịch thành một số "plugin" sau đó dlopen (3) -ed theo cùng một quy trình (và trên các máy hiện tại, điều này đủ nhanh để tương thích với một REPL tương tác , xem cái này )


Tôi thực sự khuyên bạn nên đọc cẩm nang GC . Toàn bộ cuốn sách là cần thiết để trả lời . Trước đó, hãy đọc wiki của Bộ sưu tập Rác (mà tôi cho rằng bạn đã đọc trước khi đọc bên dưới).

Các hệ thống thời gian chạy của việc thực hiện ngôn ngữ biên dịch chứa thu gom rác, và trình biên dịch được tạo mã đó là phù hợp với hệ thống thời gian chạy cụ thể. Cụ thể, các nguyên hàm cấp phát (được biên dịch theo mã máy) sẽ (hoặc có thể) gọi hệ thống thời gian chạy.

Vì vậy, làm thế nào chương trình biên dịch có thể được thu thập rác?

Chỉ bằng cách phát ra mã máy sử dụng (và "thân thiện" và "tương thích với") hệ thống thời gian chạy.

Lưu ý rằng bạn có thể tìm thấy một số thư viện thu gom rác, đặc biệt là Boehm GC , Ravenbrook's MPS hoặc thậm chí là Qish (không rõ ràng) của tôi . Và mã hóa một GC đơn giản không phải là rất khó (tuy nhiên, việc gỡ lỗi nó khó hơn và mã hóa một GC cạnh tranhkhó khăn ).

Trong một số trường hợp, trình biên dịch sẽ sử dụng một GC bảo thủ (như Boehm GC ). Sau đó, không có nhiều để mã. GC bảo thủ sẽ (khi trình biên dịch gọi thủ tục cấp phát của nó hoặc toàn bộ thường trình GC) đôi khi quét toàn bộ ngăn xếp cuộc gọi và cho rằng mọi vùng nhớ (gián tiếp) có thể truy cập được từ ngăn xếp cuộc gọi đều hoạt động. Đây được gọi là một GC bảo thủ vì thông tin gõ bị mất: nếu một số nguyên trên ngăn xếp cuộc gọi xảy ra trông giống như một số địa chỉ, nó sẽ được theo dõi, v.v.

Trong các trường hợp khác (khó khăn hơn), bộ thực thi cung cấp một bộ sưu tập rác sao chép thế hệ (một ví dụ điển hình là trình biên dịch Ocaml, biên dịch mã Ocaml thành mã máy bằng cách sử dụng một GC như vậy). Sau đó, vấn đề là tìm chính xác trên ngăn xếp cuộc gọi tất cả các con trỏ và một số trong số chúng được di chuyển bởi GC. Sau đó, trình biên dịch tạo ra siêu dữ liệu mô tả các khung ngăn xếp cuộc gọi mà thời gian chạy sử dụng. Vì vậy, các quy ước gọiABI đang trở nên cụ thể cho việc thực hiện đó (tức là trình biên dịch) & hệ thống thời gian chạy.

Trong một số trường hợp, mã máy được tạo bởi trình biên dịch (thực tế thậm chí đóng chỉ vào nó) chính là rác được thu thập . Đây là trường hợp đáng chú ý đối với SBCL (một triển khai Lisp thông thường tốt) tạo mã máy cho mỗi tương tác REPL . Điều này cũng yêu cầu một số dữ liệu meta mô tả mã và các khung cuộc gọi được sử dụng bên trong nó.

Trình biên dịch có lưu một bản sao của một số chương trình thu gom rác và dán nó vào mỗi tệp thực thi mà nó tạo ra không?

Sắp xếp Tuy nhiên, hệ thống thời gian chạy có thể là một thư viện dùng chung, v.v. Đôi khi (trên Linux và một số hệ thống POSIX khác), nó thậm chí có thể là một trình thông dịch kịch bản, ví dụ: được chuyển qua execve (2) với một shebang . Hoặc một thông dịch viên ELF , xem elf (5)PT_INTERP, v.v.

BTW, hầu hết các trình biên dịch cho ngôn ngữ với bộ sưu tập rác (và hệ thống thời gian chạy của chúng) là phần mềm miễn phí ngày nay . Vì vậy, tải về mã nguồn và nghiên cứu nó.


5
Bạn có nghĩa là có nhiều triển khai ngôn ngữ lập trình mà không có một đặc tả rõ ràng. Vâng, tôi đồng ý với điều đó. Nhưng quan điểm của tôi là ngôn ngữ lập trình không phải là một phần mềm (như một số trình biên dịch hoặc một số trình thông dịch). Nó là một cái gì đó có cú pháp và ngữ nghĩa (có lẽ cả hai đều không rõ ràng).
Basile Starynkevitch

4
@KonradRudolph: Điều đó phụ thuộc hoàn toàn vào định nghĩa của bạn về "chính thức" và "đặc tả" :-D Có Đặc tả ngôn ngữ lập trình Ruby / ISO 30170: 2012 , chỉ định một tập hợp con nhỏ của giao điểm của Ruby 1.8 và 1.9. Có Ruby Spec Suite , một tập hợp các ví dụ về các trường hợp ranh giới đóng vai trò là một loại "thông số thực thi". Sau đó, Ngôn ngữ lập trình Ruby của David Flanagan và Yukihiro Matsumoto .
Jörg W Mittag

4
Ngoài ra, Tài liệu Ruby . Thảo luận về các vấn đề trên Trình theo dõi vấn đề Ruby . Thảo luận về danh sách gửi thư của ruby-core (tiếng Anh) và ruby-dev (tiếng Nhật). Kỳ vọng thông thường của cộng đồng (ví dụ: Array#[]O (1) trường hợp xấu nhất, Hash#[]là O (1) trường hợp xấu nhất được khấu hao). Và cuối cùng nhưng không kém phần quan trọng: bộ não của matz.
Jörg W Mittag

6
@KonradRudolph: Vấn đề là: ngay cả một ngôn ngữ không có đặc điểm kỹ thuật chính thức và chỉ một thực hiện duy nhất vẫn có thể được tách thành "ngôn ngữ" (các quy tắc và hạn chế trừu tượng) và "việc thực hiện" (các chương trình xử lý mã theo các quy tắc đó và những hạn chế). Và việc triển khai vẫn làm phát sinh một đặc tả, mặc dù là một thứ tầm thường, cụ thể là: "bất cứ điều gì mã làm là đặc tả". Rốt cuộc, đó là cách mà thông số kỹ thuật ISO, RubySpec và RDocs được viết ra: bằng cách chơi xung quanh MRI kỹ thuật ngược và / hoặc kỹ thuật đảo ngược.
Jörg W Mittag

1
Vui mừng bạn đã đưa lên bộ thu gom rác của Bohem. Tôi muốn giới thiệu OP nghiên cứu nó bởi vì đây là một ví dụ tuyệt vời về cách thu gom rác đơn giản, ngay cả khi "bắt vít" với trình biên dịch hiện có.
Cort Ammon

6

Đã có một số câu trả lời hay, nhưng tôi muốn xóa một số hiểu lầm đằng sau câu hỏi này.

Không có thứ gọi là "ngôn ngữ được biên dịch tự nhiên" mỗi se. Ví dụ, cùng một mã Java đã được diễn giải (và sau đó được biên dịch một phần chỉ trong thời gian chạy) trên điện thoại cũ của tôi (Java Dalvik) và được biên dịch trước thời hạn trên điện thoại mới của tôi (ART).

Sự khác biệt giữa chạy mã nguyên bản và diễn giải ít nghiêm ngặt hơn nhiều so với vẻ ngoài của nó. Cả hai đều cần một số thư viện thời gian chạy và một số hệ điều hành để làm việc (*). Mã thông dịch cần một trình thông dịch, nhưng trình thông dịch chỉ là một phần của thời gian chạy. Nhưng ngay cả điều này không nghiêm ngặt, vì bạn có thể thay thế trình thông dịch bằng trình biên dịch (chỉ trong thời gian). Để có hiệu suất tối đa, bạn có thể muốn cả hai (thời gian chạy Java trên máy tính để bàn chứa trình thông dịch và hai trình biên dịch).

Bất kể làm thế nào để chạy mã, nó nên hành xử như nhau. Phân bổ và giải phóng bộ nhớ là một nhiệm vụ cho thời gian chạy (giống như mở tệp, bắt đầu các luồng, v.v.). Trong ngôn ngữ của bạn, bạn chỉ cần viết new X()hoặc như nhau. Đặc tả ngôn ngữ cho biết những gì sẽ xảy ra và thời gian chạy thực hiện nó.

Một số bộ nhớ trống được cấp phát, hàm tạo được gọi, v.v. Khi không có đủ bộ nhớ, thì trình thu gom rác sẽ được gọi. Vì bạn đã có trong thời gian chạy, là một đoạn mã riêng, sự tồn tại của một trình thông dịch hoàn toàn không thành vấn đề.

Thực sự không có kết nối trực tiếp giữa phiên dịch mã và thu gom rác. Chỉ có các ngôn ngữ cấp thấp như C được thiết kế để kiểm soát tốc độ và chi tiết tốt mọi thứ, không phù hợp với ý tưởng về mã không bản địa hoặc trình thu gom rác. Vì vậy, chỉ có một mối tương quan.

Điều này rất đúng trong thời xưa, trong đó, ví dụ trình thông dịch Java rất chậm và trình thu gom rác khá kém hiệu quả. Ngày nay, mọi thứ đã khác đi rất nhiều và việc nói về một ngôn ngữ được giải thích đã mất đi mọi ý nghĩa.


(*) Ít nhất là khi nói về mã mục đích chung, hãy bỏ qua các bộ tải khởi động và tương tự.


Cả Ocaml và SBCL đều là trình biên dịch riêng. Vì vậy, có những triển khai "ngôn ngữ được biên dịch".
Basile Starynkevitch

@BasileStarynkevitch BÂY GIỜ? Làm thế nào để đặt tên cho một số trình biên dịch ít nổi tiếng có liên quan đến câu trả lời của tôi? Không phải SBCL là trình biên dịch cho ngôn ngữ được giải thích ban đầu là một đối số có lợi cho tuyên bố của tôi rằng sự khác biệt đó không có ý nghĩa gì?
maaartinus

Lisp thông thường (hoặc bất kỳ ngôn ngữ nào khác) không được giải thích hoặc biên dịch. Nó là một ngôn ngữ lập trình (một đặc điểm kỹ thuật). Việc thực hiện của nó có thể là một trình biên dịch, hoặc một trình thông dịch, hoặc một cái gì đó ở giữa (ví dụ như một trình thông dịch mã byte). SBCL là một triển khai được biên dịch tương tác của Common Lisp. Ocaml cũng là một ngôn ngữ lập trình (có cả trình thông dịch mã byte và trình biên dịch gốc làm triển khai).
Stilenkevitch Basile

@BasileStarynkevitch Đó là những gì tôi đang tuyên bố. 1. Không có thứ gọi là ngôn ngữ được dịch hoặc biên dịch (mặc dù C hiếm khi được giải thích và LISP thường hiếm khi được biên dịch, nhưng điều này không thực sự quan trọng). 2. Có các triển khai được giải thích, biên dịch và hỗn hợp cho hầu hết các ngôn ngữ nổi tiếng và không có ngôn ngữ nào ngăn cản việc biên dịch hoặc giải thích.
maaartinus

6
Tôi nghĩ rằng lập luận của bạn có rất nhiều ý nghĩa. Điểm mấu chốt để tìm hiểu là bạn luôn chạy một "chương trình gốc" hoặc "không bao giờ", tuy nhiên bạn muốn xem nó. Không có exe trên Windows là có thể thực thi được; nó cần một trình tải và các tính năng khác của hệ điều hành để khởi động và thực sự cũng được "diễn giải" một phần. Điều này trở nên rõ ràng hơn với .net thực thi. java myproglà nhiều hoặc ít bản địa như grep myname /etc/passwdhoặc ld.so myprog: Đó là một tệp thực thi (bất kể điều đó có nghĩa là gì) có một đối số và thực hiện các hoạt động với dữ liệu.
Peter A. Schneider

3

Các chi tiết khác nhau giữa các lần triển khai, nhưng nhìn chung một số kết hợp sau:

  • Một thư viện thời gian chạy bao gồm một GC. Điều này sẽ xử lý cấp phát bộ nhớ và có một số điểm nhập khác, bao gồm chức năng "GC_now".
  • Trình biên dịch sẽ tạo các bảng cho GC để nó biết các trường trong đó các kiểu dữ liệu được tham chiếu. Điều này cũng sẽ được thực hiện cho các khung ngăn xếp cho từng chức năng để GC có thể theo dõi từ ngăn xếp.
  • Nếu GC tăng dần (hoạt động của GC được xen kẽ với chương trình) hoặc đồng thời (chạy trong một luồng riêng) thì trình biên dịch cũng sẽ bao gồm mã đối tượng đặc biệt để cập nhật cấu trúc dữ liệu GC khi các tham chiếu được cập nhật. Hai có vấn đề tương tự cho sự thống nhất dữ liệu.

Trong GC tăng dần và đồng thời, mã được biên dịch và GC cần hợp tác để duy trì một số bất biến. Ví dụ, trong trình thu thập sao chép, GC hoạt động bằng cách sao chép dữ liệu trực tiếp từ không gian A sang không gian B, để lại rác. Đối với chu kỳ tiếp theo, nó lật A và B và lặp lại. Vì vậy, một quy tắc có thể là đảm bảo rằng bất cứ khi nào chương trình người dùng cố gắng tham chiếu đến một đối tượng trong không gian A, điều này được phát hiện và đối tượng được sao chép ngay lập tức vào không gian B, nơi chương trình có thể tiếp tục truy cập vào nó. Một địa chỉ chuyển tiếp được để lại trong không gian A để chỉ ra cho GC rằng điều này đã xảy ra để mọi tham chiếu khác đến đối tượng được cập nhật khi chúng được theo dõi. Điều này được gọi là một "rào cản đọc".

Các thuật toán GC đã được nghiên cứu từ những năm 60, và có nhiều tài liệu về chủ đề này. Google nếu bạn muốn biết thêm thông tin.

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.