Bagaimana cara kerja pengumpulan sampah dalam bahasa yang dikompilasi secara asli?

79

Setelah menelusuri beberapa jawaban Stack Overflow, jelas bahwa beberapa bahasa yang dikompilasi secara asli memiliki pengumpulan sampah . Tetapi tidak jelas bagi saya bagaimana ini akan bekerja.

Saya mengerti bagaimana pengumpulan sampah dapat bekerja dengan bahasa yang ditafsirkan. Pengumpul sampah hanya akan berjalan bersama penerjemah dan menghapus objek yang tidak terpakai dan tidak terjangkau dari memori program. Mereka berdua berjalan bersama.

Bagaimana cara kerjanya dengan bahasa yang dikompilasi? Pemahaman saya adalah bahwa begitu kompiler telah mengkompilasi kode sumber ke kode target - khususnya kode mesin asli - selesai. Pekerjaannya selesai. Jadi bagaimana bisa program yang dikompilasi menjadi sampah juga dikumpulkan?

Apakah kompiler bekerja dengan CPU dalam beberapa cara sementara program dijalankan untuk menghapus objek "sampah"? Atau apakah kompiler menyertakan beberapa pengumpul sampah minimal dalam program yang dapat dieksekusi.

Saya percaya pernyataan terakhir saya akan memiliki lebih banyak validitas daripada yang pertama karena kutipan dari jawaban ini di Stack Overflow :

Salah satu bahasa pemrograman tersebut adalah Eiffel. Sebagian besar kompiler Eiffel menghasilkan kode C untuk alasan portabilitas. Kode C ini digunakan untuk menghasilkan kode mesin oleh kompiler C standar. Implementasi Eiffel menyediakan GC (dan kadang-kadang bahkan GC akurat) untuk kode yang dikompilasi ini, dan tidak perlu untuk VM. Khususnya, kompiler VisualEiffel menghasilkan kode mesin x86 asli secara langsung dengan dukungan GC penuh .

Pernyataan terakhir tampaknya menyiratkan bahwa kompiler menyertakan beberapa program dalam executable akhir yang bertindak sebagai pengumpul sampah saat program sedang berjalan.

Halaman di situs web bahasa D tentang pengumpulan sampah - yang secara asli dikompilasi dan memiliki pengumpul sampah opsional - juga tampaknya mengisyaratkan bahwa beberapa program latar belakang berjalan berdampingan dengan program yang dapat dieksekusi asli untuk mengimplementasikan pengumpulan sampah.

D adalah bahasa pemrograman sistem dengan dukungan untuk pengumpulan sampah. Biasanya tidak perlu membebaskan memori secara eksplisit. Alokasikan saja sesuai kebutuhan, dan pemulung akan secara berkala mengembalikan semua memori yang tidak digunakan ke kumpulan memori yang tersedia.

Jika metode yang disebutkan di atas adalah digunakan, bagaimana tepatnya itu akan berhasil? Apakah kompiler menyimpan salinan dari beberapa program pengumpulan sampah dan menempelkannya ke setiap executable yang dihasilkannya?

Atau apakah saya cacat dalam pemikiran saya? Jika demikian, metode apa yang digunakan untuk menerapkan pengumpulan sampah untuk bahasa yang dikompilasi dan bagaimana cara kerjanya?

Christian Dean
sumber
1
Saya akan menghargai jika pemilih dekat dari pertanyaan ini dapat menyatakan dengan tepat apa yang salah sehingga saya dapat memperbaikinya?
Christian Dean
6
Jika Anda menerima kenyataan bahwa GC pada dasarnya adalah bagian dari perpustakaan yang diperlukan oleh implementasi bahasa pemrograman tertentu, maka inti pertanyaan Anda tidak ada hubungannya dengan GC per se dan segala sesuatu yang berkaitan dengan tautan statis dan dinamis .
Theodoros Chatzigiannakis
7
Anda dapat mempertimbangkan pengumpul sampah sebagai bagian dari pustaka runtime yang mengimplementasikan bahasa yang setara dengannya malloc().
Barmar
9
Operasi seorang pengumpul sampah tergantung pada karakteristik pengalokasi , bukan pada model kompilasi . Pengalokasi mengetahui setiap objek yang telah dialokasikan; itu mengalokasikan mereka. Sekarang yang Anda butuhkan adalah beberapa cara untuk mengetahui objek mana yang masih hidup , dan kolektor dapat membatalkan alokasi semua objek kecuali mereka. Tidak ada dalam deskripsi yang ada hubungannya dengan model kompilasi.
Eric Lippert
1
GC adalah fitur memori dinamis, bukan fitur juru bahasa.
Dmitry Grigoryev

Jawaban:

52

Pengumpulan sampah dalam bahasa yang dikompilasi bekerja dengan cara yang sama seperti dalam bahasa yang ditafsirkan. Bahasa seperti Go menggunakan pelacakan pengumpul sampah meskipun kode mereka biasanya dikompilasi ke kode mesin sebelumnya.

(Tracing) pengumpulan sampah biasanya dimulai dengan berjalan di tumpukan panggilan dari semua utas yang saat ini berjalan. Benda-benda di tumpukan itu selalu hidup. Setelah itu, pengumpul sampah melintasi semua objek yang ditunjuk oleh objek hidup, hingga seluruh grafik objek hidup ditemukan.

Jelas bahwa melakukan ini memerlukan informasi tambahan yang tidak disediakan oleh bahasa seperti C. Secara khusus, ini membutuhkan peta bingkai stack dari masing-masing fungsi yang berisi offset semua pointer (dan mungkin datatypes mereka) serta peta semua tata letak objek yang berisi informasi yang sama.

Namun mudah untuk melihat bahwa bahasa yang memiliki jaminan tipe kuat (mis. Jika pointer dilemparkan ke tipe data yang berbeda tidak diizinkan) memang dapat menghitung peta tersebut pada waktu kompilasi. Mereka hanya menyimpan hubungan antara alamat instruksi dan peta bingkai tumpukan dan hubungan antara tipe data dan peta tata letak objek di dalam biner. Informasi ini kemudian memungkinkan mereka untuk melakukan traversal grafik objek.

Pengumpul sampah itu sendiri tidak lebih dari sebuah perpustakaan yang terhubung ke program, mirip dengan perpustakaan standar C. Misalnya, pustaka ini bisa menyediakan fungsi yang mirip dengan malloc()yang menjalankan algoritma pengumpulan jika tekanan memori tinggi.

avdgrinten
sumber
9
Antara pustaka utilitas dan kompilasi JIT, garis antara "dikompilasi ke asli" dan "berjalan di lingkungan runtime" menjadi semakin kabur.
corsiKa
6
Hanya untuk menambahkan sedikit tentang bahasa yang tidak disertai dengan dukungan GC: Memang benar bahwa C dan bahasa lain semacam itu tidak memberikan informasi tentang tumpukan panggilan, tetapi jika Anda setuju dengan beberapa kode khusus platform (biasanya termasuk sedikit kode perakitan) masih mungkin untuk menerapkan "pengumpulan sampah konservatif". The Boehm GC adalah contoh dari ini digunakan dalam program kehidupan nyata.
Matti Virkkunen
2
@corsiKa Atau lebih tepatnya, salurannya jauh lebih berbeda. Sekarang kita melihat bahwa itu adalah konsep-konsep yang tidak berhubungan yang berbeda, dan bukan antonim satu sama lain.
Kroltan
4
Satu kompleksitas tambahan yang perlu Anda perhatikan dalam runtime yang dikompilasi vs ditafsirkan berkaitan dengan kalimat ini dalam jawaban Anda: "(Menelusuri) pengumpulan sampah biasanya dimulai dengan menyusuri tumpukan panggilan semua utas yang saat ini berjalan." Pengalaman saya menerapkan GC di lingkungan yang dikompilasi adalah bahwa melacak tumpukan tidak cukup. Titik awal biasanya menangguhkan utas cukup lama untuk dilacak dari register mereka , karena mereka mungkin memiliki referensi dalam register yang belum disimpan dalam tumpukan. Untuk penerjemah, ini biasanya tidak ...
Jules
... masalah, karena lingkungan dapat mengatur agar GC terjadi di "titik aman" di mana penerjemah tahu bahwa semua data disimpan dengan aman di tumpukan yang ditafsirkan.
Jules
123

Apakah kompiler menyimpan salinan beberapa program pengumpulan sampah dan menempelnya ke setiap executable yang dihasilkannya?

Kedengarannya tidak sopan dan aneh, tapi ya. Kompiler memiliki seluruh pustaka utilitas, yang mengandung jauh lebih banyak dari sekadar kode pengumpulan sampah, dan panggilan ke pustaka ini akan dimasukkan ke dalam setiap executable yang dibuatnya. Ini disebut perpustakaan runtime , dan Anda akan terkejut betapa banyak tugas berbeda yang biasanya dilayani.

Kilian Foth
sumber
51
@ChristianDean Perhatikan bahwa bahkan C memiliki pustaka runtime. Meskipun tidak memiliki GC, masih menjalankan manajemen memori melalui pustaka runtime itu: malloc()dan free()tidak dibangun ke bahasa, bukan bagian dari sistem operasi, tetapi fungsi di pustaka ini. C ++ juga terkadang dikompilasi dengan perpustakaan pengumpulan sampah, meskipun bahasa itu tidak dirancang dengan GC dalam pikiran.
amon
18
C ++ juga berisi pustaka runtime yang melakukan hal-hal seperti membuat dynamic_castdan pengecualian berfungsi, bahkan jika Anda tidak menambahkan GC.
Sebastian Redl
23
Pustaka runtime tidak perlu disalin ke setiap executable (yang disebut penghubung statis), hanya dapat dirujuk (jalur ke biner yang berisi pustaka) dan diakses pada waktu eksekusi: ini adalah penghubung dinamis.
mouviciel
16
Kompiler juga tidak diharuskan untuk langsung masuk ke titik masuk program Anda tanpa terjadi hal lain. Saya membuat perkiraan berpendidikan bahwa setiap kompiler benar-benar menyisipkan sekelompok kode inisialisasi platform khusus sebelum ia memanggil main(), dan itu sah untuk, katakanlah, jalankan utas GC dalam kode ini. (Dengan asumsi GC tidak dilakukan di dalam panggilan alokasi memori.) Saat runtime, GC hanya benar-benar perlu tahu bagian mana dari objek yang merupakan pointer atau referensi objek, dan kompiler perlu memancarkan kode untuk menerjemahkan referensi objek ke pointer. jika GC memindahkan objek.
millimoose
15
@ millimoose: Ya. Misalnya, di GCC, kode ini adalah crt0.o(Yang merupakan kependekan dari " C R un T ime, the basics"), yang dihubungkan dengan setiap program (atau setidaknya setiap program yang tidak berdiri sendiri ).
Jörg W Mittag
58

Atau apakah kompiler menyertakan beberapa pengumpul sampah minimal dalam kode program yang dikompilasi.

Itu cara yang aneh untuk mengatakan "kompiler menghubungkan program dengan perpustakaan yang melakukan pengumpulan sampah". Tapi ya, itulah yang terjadi.

Ini tidak istimewa: kompiler biasanya menghubungkan banyak perpustakaan ke dalam program yang mereka kompilasi; jika tidak, program yang dikompilasi tidak dapat melakukan banyak hal tanpa menerapkan kembali banyak hal dari awal: bahkan menulis teks ke layar / file / ... memerlukan perpustakaan.

Tapi mungkin GC berbeda dari perpustakaan lain ini, yang menyediakan API eksplisit yang dipanggil pengguna?

Tidak: di sebagian besar bahasa, pustaka runtime melakukan banyak pekerjaan di belakang layar tanpa API yang menghadap publik, di luar GC. Perhatikan tiga contoh ini:

  1. Perkecualian perkecualian dan susun panggilan unwinding / destructor.
  2. Alokasi memori dinamis (yang biasanya tidak hanya memanggil fungsi, seperti dalam C, bahkan ketika tidak ada pengumpulan sampah).
  3. Pelacakan informasi tipe dinamis (untuk gips dll).

Jadi perpustakaan pengumpulan sampah sama sekali tidak istimewa, dan apriori tidak ada hubungannya dengan apakah suatu program dikompilasi sebelumnya.

Konrad Rudolph
sumber
ini tampaknya tidak menawarkan sesuatu yang substansial atas poin yang dibuat dan dijelaskan dalam jawaban teratas yang diposting 3 jam sebelumnya
Agas
11
@gnat Saya merasa itu berguna / perlu karena jawaban atas tidak cukup kuat sejauh ini: itu menyebutkan fakta yang sama, tetapi gagal untuk menunjukkan bahwa memilih pengumpulan sampah adalah perbedaan yang sepenuhnya buatan. Pada dasarnya, asumsi OP cacat, dan jawaban teratas tidak menyebutkan ini. Milik saya (sambil menghindari istilah yang agak kasar "cacat").
Konrad Rudolph
Ini tidak semua yang istimewa, tapi saya akan mengatakan itu agak istimewa, karena biasanya orang menganggap perpustakaan sebagai sesuatu yang secara eksplisit mereka panggil dari kode mereka; daripada implementasi semantik bahasa mendasar. Saya pikir asumsi OP yang salah di sini adalah bahwa kompiler hanya menerjemahkan kode dengan cara yang kurang lebih langsung, daripada instrumen dengan panggilan perpustakaan penulis tidak menentukan.
millimoose
7
Pustaka Runtime @millimoose Runtime beroperasi di belakang layar dalam banyak cara tanpa interaksi pengguna yang eksplisit. Pertimbangkan propagasi pengecualian dan susun panggilan unwinding / destructor. Pertimbangkan alokasi memori dinamis (yang biasanya tidak hanya memanggil fungsi, seperti dalam C, bahkan ketika tidak ada pengumpulan sampah). Pertimbangkan penanganan informasi tipe dinamis (untuk gips dll). Jadi GC benar-benar tidak unik.
Konrad Rudolph
3
Ya, saya akui saya telah mengatakan itu dengan aneh. Itu hanya karena saya skeptis terhadap kompiler yang sebenarnya melakukan sesuatu seperti itu. Tapi sekarang saya berpikir tentang hal itu, itu jauh lebih masuk akal. Kompiler dapat dengan mudah menghubungkan seorang pengumpul sampah seperti bagian lain dari perpustakaan standar. Saya percaya beberapa kebingungan saya berasal dari memikirkan seorang pengumpul sampah hanya sebagai bagian dari implementasi juru bahasa dan bukan program terpisah dalam haknya sendiri.
Christian Dean
23

Bagaimana cara kerjanya dengan bahasa yang dikompilasi?

Kata-kata Anda salah. Sebuah bahasa pemrograman adalah spesifikasi yang ditulis dalam beberapa laporan teknis (untuk contoh yang baik, lihat R5RS ). Sebenarnya Anda mengacu pada beberapa implementasi bahasa tertentu (yang merupakan perangkat lunak).

(beberapa bahasa pemrograman memiliki spesifikasi yang buruk, atau bahkan hilang, atau hanya sesuai dengan beberapa implementasi sampel; masih, bahasa pemrograman mendefinisikan perilaku - misalnya memiliki sintaks dan semantik -, itu bukan produk perangkat lunak, tetapi dapat berupa diimplementasikan oleh beberapa produk perangkat lunak; banyak bahasa pemrograman memiliki beberapa implementasi; khususnya, "dikompilasi" adalah kata sifat yang berlaku untuk implementasi - bahkan jika beberapa bahasa pemrograman lebih mudah diimplementasikan oleh penerjemah daripada oleh kompiler.)

Pemahaman saya adalah bahwa begitu kompiler telah mengkompilasi kode sumber ke kode target - khususnya kode mesin asli - selesai. Pekerjaannya selesai.

Perhatikan bahwa penerjemah dan penyusun memiliki makna yang longgar, dan beberapa implementasi bahasa dapat dianggap sebagai keduanya. Dengan kata lain, ada kontinum di antaranya. Baca Buku Naga terbaru dan pikirkan tentang bytecode , kompilasi JIT , memancarkan kode C dinamis yang dikompilasi ke dalam beberapa "plugin" lalu dlopen (3) -dengan proses yang sama (dan pada mesin saat ini, ini cukup cepat untuk kompatibel dengan REPL interaktif , lihat ini )


Saya sangat merekomendasikan membaca buku pegangan GC . Seluruh buku diperlukan untuk menjawab . Sebelum itu, baca wikipage Pengumpulan Sampah (yang saya anggap sudah Anda baca sebelum membaca di bawah).

Sistem runtime dari implementasi bahasa yang dikompilasi berisi pengumpul sampah, dan kompiler menghasilkan kode yang sesuai dengan sistem runtime tertentu. Secara khusus, alokasi primitif (dikompilasi ke kode mesin yang) akan (atau mungkin) memanggil sistem runtime.

Jadi bagaimana bisa program yang dikompilasi menjadi sampah juga dikumpulkan?

Hanya dengan memancarkan kode mesin yang menggunakan (dan "ramah" dan "kompatibel dengan") sistem runtime.

Perhatikan bahwa Anda dapat menemukan beberapa perpustakaan pengumpulan sampah, khususnya Boehm GC , MPS Ravenbrook , atau bahkan Qish saya (yang tidak dirawat ) . Dan mengkode GC sederhana tidak terlalu sulit (namun, men-debug lebih sulit, dan mengkode GC kompetitif sulit ).

Dalam beberapa kasus, kompiler akan menggunakan GC konservatif (seperti Boehm GC ). Kemudian, tidak ada banyak kode. GC konservatif akan (ketika kompiler memanggil rutin alokasi, atau seluruh rutin GC) kadang-kadang memindai seluruh tumpukan panggilan , dan menganggap bahwa setiap zona memori (secara tidak langsung) yang dapat dijangkau dari tumpukan panggilan itu aktif. Ini disebut GC konservatif karena informasi pengetikan hilang: jika bilangan bulat pada tumpukan panggilan terlihat seperti beberapa alamat, itu akan diikuti, dll.

Dalam kasus lain (lebih sulit), runtime menyediakan pengumpulan sampah penyalinan generasional (contoh khasnya adalah kompiler Ocaml, yang mengkompilasi kode Ocaml ke kode mesin menggunakan GC semacam itu). Maka masalahnya adalah menemukan tepatnya pada tumpukan panggilan semua petunjuk, dan beberapa dari mereka digerakkan oleh GC. Kemudian kompiler menghasilkan meta-data yang menggambarkan frame panggilan stack, yang digunakan runtime. Jadi konvensi pemanggilan dan ABI menjadi spesifik untuk implementasi itu (yaitu kompiler) & sistem runtime.

Dalam beberapa kasus, kode mesin yang dihasilkan oleh kompiler (sebenarnya bahkan penutup menunjuk ke sana) adalah sampah yang dikumpulkan . Ini khususnya kasus untuk SBCL (implementasi Common Lisp yang baik) yang menghasilkan kode mesin untuk setiap interaksi REPL . Ini juga membutuhkan beberapa meta-data yang menggambarkan kode dan bingkai panggilan yang digunakan di dalamnya.

Apakah kompiler menyimpan salinan dari beberapa program pengumpulan sampah dan menempelkannya ke setiap executable yang dihasilkannya?

Sortir-dari. Namun, sistem runtime dapat berupa pustaka bersama, dll. Kadang-kadang (di Linux dan beberapa sistem POSIX lainnya) itu bahkan bisa menjadi juru bahasa skrip, misalnya diteruskan ke mengeksekusi (2) dengan shebang . Atau juru bahasa ELF , lihat peri (5) dan PT_INTERP, dll.

BTW, kebanyakan kompiler untuk bahasa dengan pengumpulan sampah (dan sistem runtime mereka) saat ini adalah perangkat lunak gratis . Jadi unduh kode sumber dan pelajari.

Basile Starynkevitch
sumber
5
Maksud Anda, ada banyak implementasi bahasa pemrograman tanpa spesifikasi eksplisit. Ya saya setuju dengan itu. Tetapi poin saya adalah bahwa bahasa pemrograman bukan perangkat lunak (seperti kompiler atau juru bahasa). Ini adalah sesuatu yang memiliki sintaks dan semantik (mungkin keduanya tidak jelas).
Basile Starynkevitch
4
@KonradRudolph: Itu sepenuhnya tergantung pada definisi Anda tentang "formal" dan "spesifikasi" :-D Ada ISO / IEC 30170: 2012 Spesifikasi Bahasa Pemrograman Ruby , yang menentukan subset kecil dari persimpangan Ruby 1.8 dan 1.9. Ada Ruby Spec Suite , satu set contoh kasus batas yang berfungsi sebagai semacam "spesifikasi yang dapat dieksekusi". Kemudian, Bahasa Pemrograman Ruby oleh David Flanagan dan Yukihiro Matsumoto .
Jörg W Mittag
4
Juga, Dokumentasi Ruby . Diskusi masalah pada Ruby Issue Tracker . Diskusi tentang mailinglist ruby-core (Inggris) dan ruby-dev (Jepang). Harapan akal sehat masyarakat (mis. Array#[]O (1) kasus terburuk, Hash#[]adalah O (1) amortisasi kasus terburuk). Dan yang terakhir: otak matz.
Jörg W Mittag
6
@KonradRudolph: Intinya adalah: bahkan bahasa tanpa spesifikasi formal dan hanya inplementasi tunggal masih dapat dipisahkan menjadi "bahasa" (aturan dan batasan abstrak) dan "implementasi" (program pemrosesan kode sesuai dengan aturan dan pembatasan). Dan implementasinya masih memunculkan spesifikasi, meskipun sepele, yaitu: "apa pun yang dikerjakan kode adalah spesifikasi". Begitulah bagaimana spec ISO, RubySpec, dan RDocs ditulis: dengan bermain-main dengan dan / atau MRI rekayasa terbalik.
Jörg W Mittag
1
Senang Anda membawa pengumpul sampah Bohem. Saya akan merekomendasikan OP mempelajarinya karena ini adalah contoh yang sangat baik tentang bagaimana pengumpulan sampah sederhana, bahkan ketika "dibaut" ke kompiler yang ada.
Cort Ammon
6

Sudah ada beberapa jawaban bagus, tetapi saya ingin menghapus beberapa kesalahpahaman di balik pertanyaan ini.

Tidak ada yang namanya "bahasa kompilasi asli" per se. Misalnya, kode Java yang sama ditafsirkan (dan sebagian dikompilasi tepat waktu saat runtime) di ponsel lama saya (Java Dalvik) dan dikompilasi di telepon baru saya (ART).

Perbedaan antara menjalankan kode secara asli dan diinterpretasikan jauh lebih ketat daripada yang terlihat. Keduanya membutuhkan pustaka runtime dan beberapa sistem operasi untuk bekerja (*). Kode yang ditafsirkan membutuhkan penerjemah, tetapi penerjemah hanyalah bagian dari runtime. Tetapi meskipun ini tidak ketat, karena Anda dapat mengganti juru bahasa dengan kompiler (tepat waktu). Untuk kinerja maksimum, Anda mungkin menginginkan keduanya (desktop Java runtime berisi interpreter dan dua kompiler).

Tidak masalah bagaimana menjalankan kodenya, ia harus berperilaku sama. Mengalokasikan dan membebaskan memori adalah tugas untuk runtime (seperti halnya membuka file, memulai utas, dll.). Dalam bahasa Anda, Anda hanya menulis new X()atau sama. Spesifikasi bahasa mengatakan apa yang harus terjadi dan runtime yang melakukannya.

Sebagian memori bebas dialokasikan, konstruktor dipanggil, dll. Ketika tidak ada cukup memori, maka pemulung akan dipanggil. Karena Anda sudah berada dalam runtime, yang merupakan bagian asli kode, keberadaan juru bahasa tidak masalah sama sekali.

Benar-benar tidak ada koneksi langsung antara penafsiran kode dan pengumpulan sampah. Hanya saja bahasa tingkat rendah seperti C dirancang untuk kontrol kecepatan dan segala sesuatu yang halus, yang tidak cocok dengan gagasan kode non-asli atau pengumpul sampah. Jadi hanya ada korelasi.

Ini sangat benar di masa lalu, di mana misalnya penerjemah Jawa sangat lambat dan pengumpul sampah agak tidak efisien. Saat ini, banyak hal berbeda dan berbicara tentang bahasa yang ditafsirkan telah kehilangan akal.


(*) Setidaknya ketika berbicara tentang kode tujuan umum, kesampingkan boot loader dan sejenisnya.

maaartinus
sumber
Baik Ocaml dan SBCL adalah kompiler asli. Jadi ada yang "bahasa native dikompilasi" implementasi.
Basile Starynkevitch
@BasileStarynkevitch WAT? Bagaimana penamaan beberapa kompiler yang kurang terkenal terkait dengan jawaban saya? Bukankah SBCL sebagai kompiler untuk bahasa yang awalnya ditafsirkan sebagai argumen yang mendukung klaim saya bahwa perbedaan itu tidak masuk akal?
maaartinus
Gangguan Umum (atau bahasa lainnya) tidak diartikan atau dikompilasi. Ini adalah bahasa pemrograman (spesifikasi). Implementasinya dapat berupa kompiler, atau penerjemah, atau sesuatu di antaranya (mis. Penerjemah bytecode). SBCL adalah implementasi interaktif yang dikompilasi dari Common Lisp. Ocaml juga merupakan bahasa pemrograman (dengan interpreter bytecode dan kompiler asli sebagai implementasinya).
Basile Starynkevitch
@ BasileStarynkevitch Itulah yang saya klaim. 1. Tidak ada bahasa yang ditafsirkan atau dikompilasi (meskipun C jarang ditafsirkan dan LISP dulu jarang dikompilasi, tetapi ini tidak terlalu penting). 2. Ada implementasi yang ditafsirkan, dikompilasi dan dicampur untuk sebagian besar bahasa yang terkenal dan tidak ada bahasa yang menghalangi kompilasi atau interpretasi.
maaartinus
6
Saya pikir argumen Anda sangat masuk akal. Titik kunci untuk grok adalah bahwa Anda selalu menjalankan "program asli", atau "tidak pernah", namun Anda ingin melihatnya. Tidak ada exe di Windows yang dapat dieksekusi; perlu loader dan fitur OS lainnya hanya untuk memulai dan sebenarnya sebagian "ditafsirkan" juga. Ini menjadi lebih jelas dengan .net executable. java myprogadalah asli sebanyak atau sesedikit grep myname /etc/passwdatau ld.so myprog: Ini adalah executable (apa pun artinya) yang mengambil argumen dan melakukan operasi dengan data.
Peter - Reinstate Monica
3

Detailnya bervariasi di antara implementasi, tetapi umumnya beberapa kombinasi dari yang berikut:

  • Pustaka runtime yang mencakup GC. Ini akan menangani alokasi memori dan memiliki beberapa titik masuk lainnya, termasuk fungsi "GC_now".
  • Compiler akan membuat tabel untuk GC sehingga ia tahu bidang mana tipe data yang menjadi referensi. Ini juga akan dilakukan untuk frame tumpukan untuk setiap fungsi sehingga GC dapat melacak dari tumpukan.
  • Jika GC bersifat inkremental (aktivitas GC disatukan dengan program) atau bersamaan (berjalan di utas terpisah) maka kompiler juga akan menyertakan kode objek khusus untuk memperbarui struktur data GC saat referensi diperbarui. Keduanya memiliki masalah serupa untuk konsistensi data.

Dalam GC tambahan dan bersamaan kode yang dikompilasi dan GC perlu bekerja sama untuk mempertahankan beberapa invarian. Misalnya dalam kolektor penyalinan, GC bekerja dengan menyalin data langsung dari ruang A ke ruang B, meninggalkan sampah. Untuk siklus berikutnya ia membalik A dan B dan berulang. Jadi satu aturan dapat memastikan bahwa setiap saat program pengguna mencoba merujuk ke objek di ruang A ini terdeteksi dan objek akan segera disalin ke ruang B, di mana program dapat terus mengaksesnya. Alamat penerusan ditinggalkan di ruang A untuk menunjukkan kepada GC bahwa ini telah terjadi sehingga referensi lain ke objek diperbarui saat mereka dilacak. Ini dikenal sebagai "pembatas baca".

Algoritma GC telah dipelajari sejak tahun 60an, dan ada literatur yang luas tentang subjek ini. Google jika Anda ingin informasi lebih lanjut.

Paul Johnson
sumber