Apa dan di mana tumpukan dan tumpukan itu?

8105

Buku-buku bahasa pemrograman menjelaskan bahwa tipe nilai dibuat di stack , dan tipe referensi dibuat di heap , tanpa menjelaskan apa dua hal ini. Saya belum membaca penjelasan yang jelas tentang ini. Saya mengerti apa itu stack . Tapi,

  • Di mana dan apa mereka (secara fisik dalam memori komputer nyata)?
  • Sejauh mana mereka dikendalikan oleh OS atau bahasa run-time?
  • Apa ruang lingkup mereka?
  • Apa yang menentukan ukuran masing-masing?
  • Apa yang membuat seseorang lebih cepat?
mattshane
sumber
175
penjelasan yang sangat bagus dapat ditemukan di sini. Apa perbedaan antara tumpukan dan tumpukan?
Songo
12
Juga (sangat) bagus: codeproject.com/Articles/76153/… (bagian stack / heap)
Ben
3
Terkait, lihat Stack Clash . Remediasi Stack Clash memengaruhi beberapa aspek seperti variabel sistem dan perilaku rlimit_stack. Lihat juga Masalah
jww
3
@ mattshane Definisi stack dan heap tidak bergantung pada nilai dan tipe referensi apa pun. Dengan kata lain, stack dan heap dapat didefinisikan sepenuhnya walaupun nilai dan tipe referensi tidak pernah ada. Lebih lanjut, ketika memahami nilai dan tipe referensi, stack hanyalah detail implementasi. Per Eric Lippert: Stack Adalah Detail Implementasi, Bagian Satu .
Matius

Jawaban:

5966

Tumpukan adalah memori yang disisihkan sebagai ruang awal untuk utas eksekusi. Ketika suatu fungsi dipanggil, sebuah blok dicadangkan di atas tumpukan untuk variabel lokal dan beberapa data pembukuan. Ketika fungsi itu kembali, blok menjadi tidak digunakan dan dapat digunakan saat suatu fungsi dipanggil. Tumpukan selalu dicadangkan dalam urutan LIFO (last in first out); blok yang paling baru dipesan selalu blok berikutnya yang akan dibebaskan. Ini membuatnya sangat sederhana untuk melacak tumpukan; membebaskan blok dari tumpukan tidak lebih dari menyesuaikan satu pointer.

Heap adalah memori yang disisihkan untuk alokasi dinamis. Berbeda dengan stack, tidak ada pola yang dipaksakan untuk alokasi dan deallokasi blok dari heap; Anda dapat mengalokasikan blok kapan saja dan membebaskannya kapan saja. Ini membuatnya jauh lebih kompleks untuk melacak bagian mana dari tumpukan yang dialokasikan atau gratis pada waktu tertentu; ada banyak pengalokasi tumpukan kustom yang tersedia untuk menyesuaikan kinerja tumpukan untuk pola penggunaan yang berbeda.

Setiap utas mendapat tumpukan, sementara biasanya hanya ada satu tumpukan untuk aplikasi (meskipun tidak jarang memiliki banyak tumpukan untuk berbagai jenis alokasi).

Untuk menjawab pertanyaan Anda secara langsung:

Sejauh mana mereka dikendalikan oleh runtime OS atau bahasa?

OS mengalokasikan tumpukan untuk setiap utas tingkat sistem saat utas dibuat. Biasanya OS dipanggil oleh runtime bahasa untuk mengalokasikan heap untuk aplikasi.

Apa ruang lingkup mereka?

Tumpukan dilampirkan ke utas, jadi ketika utas keluar, tumpukan diambil kembali. Tumpukan biasanya dialokasikan pada startup aplikasi oleh runtime, dan direklamasi ketika aplikasi (secara teknis memproses) keluar.

Apa yang menentukan ukuran masing-masing?

Ukuran tumpukan diatur saat utas dibuat. Ukuran heap diatur pada startup aplikasi, tetapi dapat tumbuh sesuai ruang yang dibutuhkan (pengalokasi meminta lebih banyak memori dari sistem operasi).

Apa yang membuat seseorang lebih cepat?

Tumpukan lebih cepat karena pola akses membuatnya sepele untuk mengalokasikan dan membatalkan alokasi memori dari itu (pointer / integer hanya bertambah atau berkurang), sedangkan tumpukan memiliki pembukuan yang jauh lebih kompleks yang terlibat dalam alokasi atau deallokasi. Selain itu, setiap byte dalam tumpukan cenderung sering digunakan kembali yang artinya cenderung dipetakan ke cache prosesor, membuatnya sangat cepat. Hit kinerja lain untuk heap adalah heap, yang sebagian besar merupakan sumber daya global, biasanya harus multi-threading aman, yaitu setiap alokasi dan deallokasi harus - biasanya - disinkronkan dengan "semua" akses heap lain dalam program.

Demonstrasi yang jelas:
Sumber gambar: vikashazrati.wordpress.com

Jeff Hill
sumber
74
Jawaban yang bagus - tapi saya pikir Anda harus menambahkan bahwa sementara stack dialokasikan oleh OS ketika proses dimulai (dengan asumsi adanya OS), itu dipertahankan sejalan dengan program. Ini adalah alasan lain mengapa stack lebih cepat, juga - operasi push dan pop biasanya merupakan satu instruksi mesin, dan mesin modern dapat melakukan setidaknya 3 dari mereka dalam satu siklus, sedangkan mengalokasikan atau membebaskan tumpukan melibatkan pemanggilan kode OS.
sqykly
276
Saya benar-benar bingung dengan diagram di bagian akhir. Saya pikir saya mendapatkannya sampai saya melihat gambar itu.
Sina Madani
10
@Anarelle prosesor menjalankan instruksi dengan atau tanpa os. Contoh yang dekat dengan hati saya adalah SNES, yang tidak memiliki panggilan API, tidak ada OS seperti yang kita kenal hari ini - tetapi ia memiliki tumpukan. Mengalokasikan pada stack adalah penjumlahan dan pengurangan pada sistem ini dan itu bagus untuk variabel yang dihancurkan ketika mereka muncul dengan kembali dari fungsi yang menciptakannya, tetapi buat itu untuk, katakanlah, sebuah konstruktor, yang hasilnya tidak bisa hanya dibuang. Untuk itu kita perlu heap, yang tidak terikat untuk memanggil dan kembali. Sebagian besar OS memiliki API, tidak ada alasan untuk melakukannya sendiri
sqykly
2
"stack adalah memori yang disisihkan sebagai ruang awal". Keren. Tapi di mana sebenarnya "disisihkan" dalam hal struktur memori Java ?? Is it Heap memory / Non-heap memory / Other (Struktur memori Java sesuai betsol.com/2017/06/… )
Jatin Shashoo
4
@JatinShashoo Java runtime, sebagai interpreter bytecode, menambahkan satu tingkat virtualisasi lagi, jadi yang Anda maksud hanyalah sudut pandang aplikasi Java. Dari sudut pandang sistem operasi, semua itu hanyalah heap, di mana proses runtime Java mengalokasikan sebagian ruangnya sebagai memori "non-heap" untuk diproses bytecode. Sisa dari tumpukan tingkat OS digunakan sebagai tumpukan tingkat aplikasi, di mana data objek disimpan.
kbec
2350

Tumpukan:

  • Disimpan dalam RAM komputer seperti tumpukan itu.
  • Variabel yang dibuat pada stack akan keluar dari ruang lingkup dan secara otomatis dialokasikan.
  • Jauh lebih cepat untuk mengalokasikan dibandingkan dengan variabel di heap.
  • Diimplementasikan dengan struktur data tumpukan aktual.
  • Menyimpan data lokal, mengembalikan alamat, digunakan untuk melewati parameter.
  • Dapat memiliki stack overflow ketika terlalu banyak stack digunakan (kebanyakan dari rekursi tak terbatas atau terlalu dalam, alokasi yang sangat besar).
  • Data yang dibuat pada stack dapat digunakan tanpa pointer.
  • Anda akan menggunakan tumpukan jika Anda tahu persis berapa banyak data yang perlu Anda alokasikan sebelum waktu kompilasi dan itu tidak terlalu besar.
  • Biasanya ukuran maksimum sudah ditentukan saat program Anda mulai.

Tumpukan:

  • Disimpan dalam RAM komputer seperti tumpukan.
  • Dalam C ++, variabel pada heap harus dihancurkan secara manual dan tidak pernah keluar dari ruang lingkup. Data tersebut dibebaskan dengan delete, delete[]atau free.
  • Lebih lambat untuk mengalokasikan dibandingkan dengan variabel pada stack.
  • Digunakan sesuai permintaan untuk mengalokasikan blok data untuk digunakan oleh program.
  • Dapat mengalami fragmentasi ketika ada banyak alokasi dan deallokasi.
  • Dalam C ++ atau C, data yang dibuat di heap akan diarahkan oleh pointer dan dialokasikan dengan newatau mallocmasing - masing.
  • Dapat mengalami kegagalan alokasi jika buffer yang terlalu besar diminta untuk dialokasikan.
  • Anda akan menggunakan heap jika Anda tidak tahu persis berapa banyak data yang akan Anda butuhkan pada saat dijalankan atau jika Anda perlu mengalokasikan banyak data.
  • Bertanggung jawab atas kebocoran memori.

Contoh:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;
Brian R. Bondy
sumber
31
Pointer pBuffer dan nilai b terletak di stack, dan sebagian besar kemungkinan dialokasikan di pintu masuk ke fungsi. Bergantung pada kompiler, buffer juga dapat dialokasikan di pintu masuk fungsi.
Andy
36
Ini adalah kesalahpahaman umum bahwa Cbahasa, sebagaimana didefinisikan oleh C99standar bahasa (tersedia di open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf ), memerlukan "tumpukan". Bahkan, kata 'tumpukan' bahkan tidak muncul dalam standar. Pernyataan ini menjawab wrt / to Cpenggunaan stack benar secara umum, tetapi sama sekali tidak diperlukan oleh bahasa. Lihat knosof.co.uk/cbook/cbook.html untuk info lebih lanjut, dan khususnya bagaimana Cditerapkan pada arsitektur ganjil seperti en.wikipedia.org/wiki/Burroughs_large_systems
johne
55
@Brian Anda harus menjelaskan mengapa buffer [] dan pointer pBuffer dibuat di stack dan mengapa data pBuffer dibuat di heap. Saya pikir beberapa ppl mungkin bingung dengan jawaban Anda karena mereka mungkin berpikir program ini secara khusus memerintahkan memori dialokasikan pada tumpukan vs tumpukan tetapi ini tidak terjadi. Apakah karena Buffer adalah tipe nilai sedangkan pBuffer adalah tipe referensi?
Howiecamp
9
@Remover: Tidak ada pointer yang memegang alamat dan dapat menunjuk ke sesuatu di heap atau stack secara merata. baru, malloc, dan beberapa fungsi lain yang mirip dengan malloc mengalokasikan pada heap dan mengembalikan alamat memori yang dialokasikan. Mengapa Anda ingin mengalokasikan di heap? Sehingga memori Anda tidak akan keluar dari ruang lingkup dan dirilis sampai Anda menginginkannya.
Brian R. Bondy
35
"Bertanggung jawab atas kebocoran memori" - Tumpukan tidak bertanggung jawab atas kebocoran memori! Lazy / Forgetful / ex-java coders / coders yang tidak peduli!
Laz
1370

Poin paling penting adalah bahwa tumpukan dan tumpukan adalah istilah umum untuk cara di mana memori dapat dialokasikan. Mereka dapat diimplementasikan dalam banyak cara yang berbeda, dan istilah ini berlaku untuk konsep dasar.

  • Dalam tumpukan item, item duduk satu di atas yang lain dalam urutan ditempatkan di sana, dan Anda hanya dapat menghapus yang atas (tanpa menjatuhkan semuanya).

    Tumpukan seperti tumpukan kertas

    Kesederhanaan tumpukan adalah bahwa Anda tidak perlu mempertahankan tabel yang berisi catatan setiap bagian dari memori yang dialokasikan; satu-satunya informasi negara yang Anda butuhkan adalah satu penunjuk ke ujung tumpukan. Untuk mengalokasikan dan menghapus alokasi, Anda hanya menambah dan mengurangi pointer tunggal itu. Catatan: tumpukan kadang-kadang dapat diimplementasikan untuk memulai di bagian atas dari memori dan memperluas ke bawah daripada tumbuh ke atas.

  • Dalam tumpukan, tidak ada urutan khusus untuk cara item ditempatkan. Anda dapat menjangkau dan menghapus item dalam urutan apa pun karena tidak ada item 'teratas' yang jelas.

    Tumpukan seperti tumpukan allsort licorice

    Alokasi tumpukan memerlukan pemeliharaan catatan penuh dari memori yang dialokasikan dan apa yang tidak, serta beberapa pemeliharaan overhead untuk mengurangi fragmentasi, menemukan segmen memori yang berdekatan cukup besar agar sesuai dengan ukuran yang diminta, dan sebagainya. Memori dapat dibatalkan alokasinya kapan saja meninggalkan ruang kosong. Kadang-kadang pengalokasi memori akan melakukan tugas perawatan seperti mendefrag memori dengan memindahkan memori yang dialokasikan, atau mengumpulkan sampah - mengidentifikasi saat runtime ketika memori tidak lagi dalam ruang lingkup dan membatalkan alokasi.

Gambar-gambar ini harus melakukan pekerjaan yang cukup baik untuk menggambarkan dua cara mengalokasikan dan membebaskan memori dalam tumpukan dan tumpukan. Yum!

  • Sejauh mana mereka dikendalikan oleh runtime OS atau bahasa?

    Seperti disebutkan, heap dan stack adalah istilah umum, dan dapat diimplementasikan dalam banyak cara. Program komputer biasanya memiliki tumpukan yang disebut tumpukan panggilan yang menyimpan informasi yang relevan dengan fungsi saat ini seperti pointer ke mana fungsi itu dipanggil, dan variabel lokal apa pun. Karena fungsi memanggil fungsi lain dan kemudian kembali, tumpukan tumbuh dan menyusut untuk menyimpan informasi dari fungsi lebih jauh ke tumpukan panggilan. Suatu program tidak benar-benar memiliki kontrol runtime atasnya; itu ditentukan oleh bahasa pemrograman, OS dan bahkan arsitektur sistem.

    Heap adalah istilah umum yang digunakan untuk setiap memori yang dialokasikan secara dinamis dan acak; yaitu rusak. Memori biasanya dialokasikan oleh OS, dengan aplikasi yang memanggil fungsi API untuk melakukan alokasi ini. Ada sedikit overhead yang diperlukan dalam mengelola memori yang dialokasikan secara dinamis, yang biasanya ditangani oleh kode runtime dari bahasa pemrograman atau lingkungan yang digunakan.

  • Apa ruang lingkup mereka?

    Tumpukan panggilan adalah konsep tingkat rendah sehingga tidak berhubungan dengan 'ruang lingkup' dalam arti pemrograman. Jika Anda membongkar beberapa kode, Anda akan melihat referensi gaya pointer relatif ke bagian-bagian stack, tetapi sejauh menyangkut bahasa tingkat yang lebih tinggi, bahasa tersebut memberlakukan aturan cakupannya sendiri. Namun, satu aspek penting dari stack adalah bahwa begitu suatu fungsi kembali, segala sesuatu yang bersifat lokal dari fungsi tersebut segera dibebaskan dari stack. Itu bekerja dengan cara yang Anda harapkan akan berfungsi mengingat cara kerja bahasa pemrograman Anda. Dalam tumpukan, juga sulit untuk didefinisikan. Cakupannya adalah apa pun yang diekspos oleh OS, tetapi bahasa pemrograman Anda mungkin menambahkan aturannya tentang apa "lingkup" dalam aplikasi Anda. Arsitektur prosesor dan OS menggunakan pengalamatan virtual, yang diterjemahkan oleh prosesor ke alamat fisik dan ada kesalahan halaman, dll. Mereka melacak halaman apa yang termasuk dalam aplikasi mana. Anda tidak pernah benar-benar perlu khawatir tentang ini, karena Anda hanya menggunakan metode apa pun yang digunakan bahasa pemrograman Anda untuk mengalokasikan dan membebaskan memori, dan memeriksa kesalahan (jika alokasi / pembebasan gagal karena alasan apa pun).

  • Apa yang menentukan ukuran masing-masing?

    Sekali lagi, itu tergantung pada bahasa, kompiler, sistem operasi dan arsitektur. Tumpukan biasanya pra-dialokasikan, karena menurut definisi itu harus memori yang berdekatan. Kompilator bahasa atau OS menentukan ukurannya. Anda tidak menyimpan potongan besar data pada stack, jadi itu akan cukup besar sehingga tidak boleh sepenuhnya digunakan, kecuali dalam kasus rekursi tak berujung yang tidak diinginkan (karenanya, "stack overflow") atau keputusan pemrograman tidak biasa lainnya.

    Tumpukan adalah istilah umum untuk apa pun yang dapat dialokasikan secara dinamis. Tergantung pada cara Anda melihatnya, ukurannya terus berubah. Dalam prosesor dan sistem operasi modern cara tepatnya cara kerjanya sangat abstrak, jadi Anda biasanya tidak perlu terlalu khawatir tentang cara kerjanya jauh di bawah, kecuali bahwa (dalam bahasa yang memungkinkan Anda), Anda tidak boleh menggunakan memori yang Anda belum mengalokasikan atau memori yang telah Anda bebaskan.

  • Apa yang membuat seseorang lebih cepat?

    Tumpukan lebih cepat karena semua memori bebas selalu bersebelahan. Tidak ada daftar yang perlu dipertahankan dari semua segmen memori bebas, hanya satu penunjuk ke bagian atas tumpukan saat ini. Compiler biasanya menyimpan pointer ini dalam register khusus yang cepat untuk keperluan ini. Terlebih lagi, operasi selanjutnya pada stack biasanya terkonsentrasi di area memori yang sangat dekat, yang pada level yang sangat rendah baik untuk optimasi oleh cache on-die prosesor.

thomasrutter
sumber
20
David Saya tidak setuju bahwa itu adalah gambar yang bagus atau "push-down stack" adalah istilah yang baik untuk menggambarkan konsep tersebut. Ketika Anda menambahkan sesuatu ke tumpukan, isi tumpukan lainnya tidak didorong ke bawah, mereka tetap di tempatnya.
thomasrutter
8
Jawaban ini termasuk kesalahan besar. Variabel statis tidak dialokasikan pada stack. Lihat jawaban saya [tautan] stackoverflow.com/a/13326916/1763801 untuk klarifikasi. Anda menyamakan variabel "otomatis" dengan variabel "statis", tetapi semuanya tidak sama
davec
13
Secara khusus, Anda mengatakan "variabel lokal yang dialokasikan secara statis" dialokasikan pada stack. Sebenarnya mereka dialokasikan di segmen data. Hanya variabel yang dialokasikan secara otomatis (yang mencakup sebagian besar tetapi tidak semua variabel lokal dan juga hal-hal seperti parameter fungsi yang diteruskan oleh nilai alih-alih dengan referensi) dialokasikan pada tumpukan.
davec
9
Saya baru saja menyadari bahwa Anda benar - dalam C, alokasi statis adalah hal yang terpisah dan bukan istilah untuk apa pun yang tidak dinamis . Saya sudah mengedit jawaban saya, terima kasih.
thomasrutter
5
Ini bukan hanya C. Java, Pascal, Python dan banyak lainnya semua memiliki gagasan alokasi statis versus otomatis versus dinamis. Mengatakan "alokasi statis" berarti hal yang sama di mana-mana. Dalam bahasa apa pun alokasi statis tidak berarti "tidak dinamis". Anda ingin alokasi "otomatis" istilah untuk apa yang Anda gambarkan (yaitu hal-hal di stack).
davec
727

(Saya telah memindahkan jawaban ini dari pertanyaan lain yang kurang lebih merupakan jawaban dari pertanyaan ini.)

Jawaban untuk pertanyaan Anda adalah implementasi spesifik dan dapat bervariasi di seluruh kompiler dan arsitektur prosesor. Namun, inilah penjelasan yang disederhanakan.

  • Baik stack dan heap adalah area memori yang dialokasikan dari sistem operasi yang mendasarinya (seringkali memori virtual yang dipetakan ke memori fisik sesuai permintaan).
  • Dalam lingkungan multi-utas, masing-masing utas akan memiliki tumpukan mandiri sepenuhnya tetapi mereka akan berbagi tumpukan. Akses bersamaan harus dikontrol pada heap dan tidak mungkin pada stack.

Tumpukan itu

  • Tumpukan berisi daftar tertaut blok yang digunakan dan gratis. Alokasi baru pada heap (oleh newatau malloc) dipenuhi dengan membuat blok yang sesuai dari salah satu blok gratis. Ini membutuhkan pembaruan daftar blok pada heap. Informasi meta ini tentang blok di heap juga disimpan di heap sering di area kecil tepat di depan setiap blok.
  • Seiring bertambahnya tumpukan, blok baru sering dialokasikan dari alamat yang lebih rendah ke alamat yang lebih tinggi. Dengan demikian Anda dapat menganggap tumpukan sebagai tumpukan blok memori yang tumbuh dalam ukuran saat memori dialokasikan. Jika tumpukan terlalu kecil untuk alokasi, ukuran seringkali dapat ditingkatkan dengan memperoleh lebih banyak memori dari sistem operasi yang mendasarinya.
  • Mengalokasikan dan menghilangkan blok banyak kecil dapat meninggalkan tumpukan dalam keadaan di mana ada banyak blok kecil gratis diselingi antara blok yang digunakan. Permintaan untuk mengalokasikan blok besar mungkin gagal karena tidak ada blok gratis yang cukup besar untuk memenuhi permintaan alokasi meskipun ukuran gabungan blok gratis mungkin cukup besar. Ini disebut fragmentasi tumpukan .
  • Ketika blok yang digunakan yang berdekatan dengan blok bebas dideallocated, blok bebas baru dapat digabungkan dengan blok bebas yang berdekatan untuk membuat blok bebas yang lebih besar secara efektif mengurangi fragmentasi tumpukan.

Tumpukan itu

Tumpukan

  • Tumpukan sering bekerja dalam tandem dekat dengan register khusus pada CPU bernama stack pointer . Awalnya, penunjuk tumpukan menunjuk ke bagian atas tumpukan (alamat tertinggi pada tumpukan).
  • CPU memiliki instruksi khusus untuk mendorong nilai - nilai ke tumpukan dan memindahkannya kembali dari tumpukan. Setiap push menyimpan nilai di lokasi saat ini dari penunjuk tumpukan dan mengurangi penunjuk tumpukan. Sebuah pop mengambil nilai yang ditunjuk oleh penunjuk tumpukan dan kemudian meningkatkan penunjuk tumpukan (jangan bingung dengan fakta bahwa menambahkan nilai ke tumpukan mengurangi penumpukan tumpukan dan menghapus nilai meningkatkannya . Ingatlah bahwa tumpukan tumbuh menjadi bagian bawah). Nilai yang disimpan dan diambil adalah nilai register CPU.
  • Ketika suatu fungsi disebut CPU menggunakan instruksi khusus yang mendorong pointer instruksi saat ini , yaitu alamat dari kode yang dieksekusi pada stack. CPU kemudian melompat ke fungsi dengan mengatur penunjuk instruksi ke alamat fungsi yang dipanggil. Kemudian, ketika fungsi kembali, penunjuk instruksi lama muncul dari tumpukan dan eksekusi dilanjutkan pada kode setelah panggilan ke fungsi.
  • Ketika suatu fungsi dimasukkan, penunjuk tumpukan dikurangi untuk mengalokasikan lebih banyak ruang pada tumpukan untuk variabel lokal (otomatis). Jika fungsi memiliki satu variabel 32 bit lokal, empat byte dikesampingkan pada stack. Ketika fungsi kembali, penunjuk tumpukan dipindahkan kembali untuk membebaskan area yang dialokasikan.
  • Jika suatu fungsi memiliki parameter, ini didorong ke tumpukan sebelum panggilan ke fungsi. Kode dalam fungsi ini kemudian dapat menavigasi tumpukan dari penunjuk tumpukan saat ini untuk menemukan nilai-nilai ini.
  • Panggilan fungsi Nesting bekerja seperti pesona. Setiap panggilan baru akan mengalokasikan parameter fungsi, alamat pengirim dan ruang untuk variabel lokal dan catatan aktivasi ini dapat ditumpuk untuk panggilan bersarang dan akan bersantai dengan cara yang benar ketika fungsi kembali.
  • Karena stack adalah blok memori terbatas, Anda dapat menyebabkan stack overflow dengan memanggil terlalu banyak fungsi bersarang dan / atau mengalokasikan terlalu banyak ruang untuk variabel lokal. Seringkali area memori yang digunakan untuk stack diatur sedemikian rupa sehingga penulisan di bawah bagian bawah (alamat terendah) stack akan memicu jebakan atau pengecualian dalam CPU. Kondisi luar biasa ini kemudian dapat ditangkap oleh runtime dan dikonversi menjadi semacam pengecualian stack overflow.

Tumpukan

Bisakah fungsi dialokasikan pada heap daripada stack?

Tidak, catatan aktivasi untuk fungsi (yaitu variabel lokal atau otomatis) dialokasikan pada tumpukan yang digunakan tidak hanya untuk menyimpan variabel-variabel ini, tetapi juga untuk melacak panggilan fungsi yang disarangkan.

Bagaimana tumpukan dikelola benar-benar hingga lingkungan runtime. Penggunaan C mallocdan penggunaan C ++ new, tetapi banyak bahasa lain memiliki pengumpulan sampah.

Namun, stack adalah fitur tingkat rendah yang terkait erat dengan arsitektur prosesor. Menumbuhkan tumpukan ketika tidak ada cukup ruang tidak terlalu sulit karena dapat diimplementasikan dalam panggilan perpustakaan yang menangani tumpukan. Namun, menumbuhkan tumpukan seringkali tidak mungkin karena stack overflow hanya ditemukan ketika sudah terlambat; dan mematikan utas eksekusi adalah satu-satunya pilihan.

Martin Liversage
sumber
35
@ Martin - Jawaban / penjelasan yang sangat bagus daripada jawaban yang lebih abstrak yang diterima. Program perakitan sampel yang menunjukkan penunjuk / register tumpukan digunakan vis a vis fungsi panggilan akan lebih ilustratif.
Bikal Lem
3
Setiap tipe referensi adalah komposisi tipe nilai (int, string dll). Seperti yang dikatakan, bahwa tipe nilai disimpan dalam stack daripada bagaimana cara kerjanya ketika mereka adalah bagian dari tipe referensi.
Nps
15
Jawaban ini adalah yang terbaik menurut saya, karena itu membantu saya memahami apa sebenarnya pernyataan pengembalian dan bagaimana hubungannya dengan "alamat pengirim" yang saya temui setiap saat, apa artinya mendorong fungsi ke stack, dan mengapa fungsi didorong ke tumpukan. Jawaban bagus!
Alex
3
Ini adalah yang terbaik menurut saya, yaitu untuk menyebutkan bahwa heap / stack sangat spesifik untuk implementasi. Jawaban lain mengasumsikan banyak hal tentang bahasa dan lingkungan / OS. +1
Qix - MONICA DISEBUTKAN
2
Apa maksud Anda "Kode dalam fungsi ini kemudian dapat menavigasi tumpukan dari penunjuk tumpukan saat ini untuk menemukan nilai-nilai ini." ? Bisakah Anda menguraikan ini?
Koray Tugay
404

Dalam kode C # berikut

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

Begini cara memori dikelola

Gambar variabel pada stack

Local Variablesyang hanya perlu bertahan selama pemanggilan fungsi masuk ke stack. Heap digunakan untuk variabel yang seumur hidupnya kita tidak benar-benar tahu di muka tetapi kita berharap mereka bertahan lama. Dalam kebanyakan bahasa, penting bagi kita untuk mengetahui pada waktu kompilasi seberapa besar variabel jika kita ingin menyimpannya di stack.

Objek (yang bervariasi dalam ukuran saat kita memperbaruinya) terus bertambah karena kita tidak tahu pada waktu pembuatan berapa lama mereka akan bertahan. Dalam banyak bahasa tumpukan adalah sampah yang dikumpulkan untuk menemukan objek (seperti objek cls1) yang tidak lagi memiliki referensi.

Di Jawa, sebagian besar objek masuk langsung ke tumpukan. Dalam bahasa seperti C / C ++, struct dan kelas seringkali dapat tetap berada di stack ketika Anda tidak berurusan dengan pointer.

Informasi lebih lanjut dapat ditemukan di sini:

Perbedaan antara tumpukan dan tumpukan memori alokasi «timmurphy.org

dan di sini:

Membuat Objek di Stack and Heap

Artikel ini adalah sumber gambar di atas: Enam konsep .NET penting: Stack, heap, tipe nilai, tipe referensi, tinju, dan unboxing - CodeProject

tapi ketahuilah itu mungkin mengandung beberapa ketidakakuratan.

Snowcrash
sumber
15
Ini salah. i dan cls bukan variabel "statis". mereka disebut variabel "lokal" atau "otomatis". Ini adalah perbedaan yang sangat penting. Lihat [tautan] stackoverflow.com/a/13326916/1763801 untuk klarifikasi
davec
9
Saya tidak mengatakan mereka adalah variabel statis . Saya mengatakan bahwa int dan cls1 adalah item statis . Memori mereka dialokasikan secara statis dan oleh karena itu mereka pergi ke stack. Ini berbeda dengan objek yang membutuhkan alokasi memori dinamis yang karenanya naik tumpukan.
Snowcrash
12
Saya mengutip "Item statis ... buka stack". Ini benar-benar salah. Item statis masuk dalam segmen data, item otomatis masuk ke stack.
davec
14
Juga siapa pun yang menulis artikel proyek kode itu tidak tahu apa yang ia bicarakan. Misalnya, ia mengatakan "yang primitif membutuhkan memori tipe statis" yang sama sekali tidak benar. Tidak ada yang menghentikan Anda mengalokasikan primitif di heap secara dinamis, cukup tulis sesuatu seperti "int array [] = new int [num]" dan voila, primitif dialokasikan secara dinamis di .NET. Itu hanya salah satu dari beberapa ketidakakuratan.
davec
8
Saya mengedit posting Anda karena Anda telah membuat kesalahan teknis serius tentang apa yang terjadi di tumpukan dan tumpukan.
Tom Leys
209

Tumpukan Ketika Anda memanggil suatu fungsi argumen untuk fungsi itu ditambah beberapa overhead lainnya diletakkan di stack. Beberapa info (seperti tempat kembali) juga disimpan di sana. Ketika Anda mendeklarasikan variabel di dalam fungsi Anda, variabel itu juga dialokasikan pada stack.

Deallocating stack sangat sederhana karena Anda selalu melakukan deallocate dalam urutan terbalik yang Anda alokasikan. Stack stuff ditambahkan saat Anda memasukkan fungsi, data yang sesuai dihapus saat Anda keluar dari fungsinya. Ini berarti Anda cenderung tetap berada dalam wilayah kecil tumpukan kecuali jika Anda memanggil banyak fungsi yang memanggil banyak fungsi lainnya (atau membuat solusi rekursif).

Heap Heap adalah nama umum untuk tempat Anda meletakkan data yang Anda buat dengan cepat. Jika Anda tidak tahu berapa banyak pesawat ruang angkasa yang akan dibuat oleh program Anda, Anda cenderung menggunakan operator baru (atau malloc atau yang setara) untuk membuat setiap pesawat ruang angkasa. Alokasi ini akan bertahan sebentar, jadi kemungkinan kita akan membebaskan barang dalam urutan yang berbeda dari yang kita buat.

Dengan demikian, tumpukan jauh lebih kompleks, karena akhirnya ada daerah memori yang tidak terpakai disisipkan dengan potongan yang - memori menjadi terfragmentasi. Menemukan memori bebas dari ukuran yang Anda butuhkan adalah masalah yang sulit. Inilah mengapa tumpukan harus dihindari (meskipun masih sering digunakan).

Implementasi Implementasi stack dan heap biasanya turun ke runtime / OS. Seringkali gim dan aplikasi lain yang sangat penting kinerjanya membuat solusi memori mereka sendiri yang mengambil banyak memori dari tumpukan dan kemudian menyajikannya secara internal untuk menghindari mengandalkan OS untuk memori.

Ini hanya praktis jika penggunaan memori Anda sangat berbeda dari norma - yaitu untuk gim di mana Anda memuat level dalam satu operasi besar dan dapat membuang semuanya jauh dalam operasi besar lainnya.

Lokasi fisik dalam memori Ini kurang relevan daripada yang Anda pikirkan karena teknologi yang disebut Memori Virtual yang membuat program Anda berpikir bahwa Anda memiliki akses ke alamat tertentu di mana data fisik berada di tempat lain (bahkan di hard disk!). Alamat yang Anda dapatkan untuk tumpukan semakin meningkat saat pohon panggilan Anda semakin dalam. Alamat untuk heap tidak dapat diprediksi (yaitu spesifik implimentation) dan terus terang tidak penting.

Tom Leys
sumber
16
Rekomendasi untuk menghindari menggunakan tumpukan cukup kuat. Sistem modern memiliki manajer tumpukan yang baik, dan bahasa dinamis modern menggunakan tumpukan secara luas (tanpa programmer benar-benar mengkhawatirkannya). Saya akan mengatakan menggunakan heap, tetapi dengan pengalokasi manual, jangan lupa untuk membebaskan!
Greg Hewgill
2
Jika Anda dapat menggunakan tumpukan atau tumpukan, gunakan tumpukan. Jika Anda tidak dapat menggunakan tumpukan, benar-benar tidak ada pilihan. Saya menggunakan keduanya banyak, dan tentu saja menggunakan std :: vector atau hits serupa heap. Untuk pemula, Anda menghindari tumpukan karena tumpukan sangat mudah !!
Tom Leys
Jika bahasa Anda tidak mengimplementasikan pengumpulan sampah, Smart pointer (Objek yang dipisahkan secara terpisah yang membungkus sebuah pointer yang melakukan penghitungan referensi untuk potongan memori yang dialokasikan secara dinamis) sangat terkait dengan pengumpulan sampah dan merupakan cara yang layak mengelola tumpukan di brankas. dan bocor secara gratis. Mereka diimplementasikan dalam berbagai kerangka kerja, tetapi juga tidak terlalu sulit untuk diterapkan untuk program Anda sendiri.
BenPen
1
"Inilah sebabnya tumpukan harus dihindari (meskipun masih sering digunakan)." Saya tidak yakin apa artinya ini secara praktis, terutama karena memori dikelola secara berbeda dalam banyak bahasa tingkat tinggi. Karena pertanyaan ini ditandai dengan agnostik-bahasa, saya akan mengatakan bahwa komentar / baris ini salah penempatan dan tidak berlaku.
LintfordPickle
2
Poin bagus @ JonnoHampson - Saat Anda membuat poin yang valid, saya berpendapat bahwa jika Anda bekerja dalam "bahasa tingkat tinggi" dengan GC Anda mungkin tidak peduli tentang mekanisme alokasi memori sama sekali - dan jadi jangan bahkan peduli apa tumpukan dan tumpukan itu.
Tom Leys
194

Untuk memperjelas, jawaban ini memiliki informasi yang salah ( thomas memperbaiki jawabannya setelah komentar, keren :)). Jawaban lain hanya menghindari menjelaskan apa artinya alokasi statis. Jadi saya akan menjelaskan tiga bentuk alokasi utama dan bagaimana mereka biasanya berhubungan dengan heap, stack, dan segmen data di bawah ini. Saya juga akan menunjukkan beberapa contoh dalam C / C ++ dan Python untuk membantu orang mengerti.

Variabel "Statis" (AKA dialokasikan secara statis) tidak dialokasikan pada stack. Jangan berasumsi begitu - banyak orang melakukannya hanya karena "statis" terdengar sangat mirip "tumpukan". Mereka sebenarnya tidak ada di tumpukan atau tumpukan. Merupakan bagian dari apa yang disebut segmen data .

Namun, umumnya lebih baik untuk mempertimbangkan " ruang lingkup " dan " seumur hidup " daripada "tumpukan" dan "tumpukan".

Lingkup mengacu pada bagian mana dari kode yang dapat mengakses variabel. Secara umum kami berpikir tentang ruang lingkup lokal (hanya dapat diakses oleh fungsi saat ini) versus ruang lingkup global (dapat diakses di mana saja) meskipun ruang lingkup bisa menjadi jauh lebih kompleks.

Seumur hidup mengacu pada ketika variabel dialokasikan dan dialokasikan selama pelaksanaan program. Biasanya kami memikirkan alokasi statis (variabel akan bertahan selama seluruh durasi program, sehingga berguna untuk menyimpan informasi yang sama di beberapa panggilan fungsi) versus alokasi otomatis (variabel hanya bertahan selama satu panggilan ke suatu fungsi, sehingga berguna untuk menyimpan informasi yang hanya digunakan selama fungsi Anda dan dapat dibuang setelah Anda selesai) versus alokasi dinamis (variabel yang durasinya ditentukan saat runtime, alih-alih waktu kompilasi seperti statis atau otomatis).

Meskipun sebagian besar kompiler dan interpreter menerapkan perilaku ini secara serupa dalam hal menggunakan tumpukan, tumpukan, dll, kompiler kadang-kadang dapat melanggar konvensi ini jika ingin selama perilaku itu benar. Misalnya, karena optimasi, variabel lokal hanya dapat ada dalam register atau dihapus seluruhnya, meskipun sebagian besar variabel lokal ada di tumpukan. Seperti yang telah ditunjukkan dalam beberapa komentar, Anda bebas menerapkan kompiler yang bahkan tidak menggunakan stack atau heap, tetapi beberapa mekanisme penyimpanan lainnya (jarang dilakukan, karena tumpukan dan tumpukan sangat bagus untuk ini).

Saya akan memberikan beberapa kode C beranotasi sederhana untuk menggambarkan semua ini. Cara terbaik untuk belajar adalah menjalankan program di bawah debugger dan melihat perilaku tersebut. Jika Anda lebih suka membaca python, lewati sampai akhir jawaban :)

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

Contoh yang sangat tajam tentang mengapa penting untuk membedakan antara masa hidup dan ruang lingkup adalah bahwa variabel dapat memiliki ruang lingkup lokal tetapi seumur hidup statis - misalnya, "someLocalStaticVariable" dalam contoh kode di atas. Variabel-variabel semacam itu dapat membuat kebiasaan penamaan kita yang umum tetapi tidak resmi sangat membingungkan. Sebagai contoh ketika kita mengatakan " lokal " kita biasanya berarti " variabel yang dialokasikan secara otomatis secara lokal " dan ketika kita mengatakan global kita biasanya berarti " variabel yang dialokasikan secara global dengan cakupan statis ". Sayangnya ketika datang ke hal-hal seperti " file scoped variabel yang dialokasikan secara statis " banyak orang hanya mengatakan ... " huh ??? ".

Beberapa pilihan sintaks dalam C / C ++ memperburuk masalah ini - misalnya banyak orang berpikir variabel global tidak "statis" karena sintaks yang ditunjukkan di bawah ini.

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

Perhatikan bahwa menempatkan kata kunci "statis" dalam deklarasi di atas mencegah var2 memiliki lingkup global. Namun demikian, global var1 memiliki alokasi statis. Ini tidak intuitif! Untuk alasan ini, saya mencoba untuk tidak pernah menggunakan kata "statis" ketika menggambarkan ruang lingkup, dan sebaliknya mengatakan sesuatu seperti ruang lingkup "file" atau "file terbatas". Namun banyak orang menggunakan frasa "statis" atau "ruang lingkup statis" untuk menggambarkan variabel yang hanya dapat diakses dari satu file kode. Dalam konteks seumur hidup, "statis" selalu berarti variabel dialokasikan pada awal program dan tidak dialokasikan ketika program keluar.

Beberapa orang menganggap konsep ini spesifik untuk C / C ++. Mereka tidak. Sebagai contoh, contoh Python di bawah ini menggambarkan ketiga jenis alokasi (ada beberapa perbedaan halus dalam bahasa yang ditafsirkan yang tidak akan saya bahas di sini).

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones
davec
sumber
Saya akan merujuk ke variabel statis yang dideklarasikan dalam fungsi sebagai hanya memiliki aksesibilitas lokal , tetapi umumnya tidak akan menggunakan istilah "ruang lingkup" dengannya. Juga, mungkin perlu dicatat bahwa aspek satu tumpukan / tumpukan dengan bahasa yang pada dasarnya memiliki fleksibilitas nol: bahasa yang menyimpan konteks eksekusi pada tumpukan tidak dapat menggunakan tumpukan yang sama untuk menampung barang-barang yang perlu hidup lebih lama dari konteks di mana mereka dibuat . Beberapa bahasa suka PostScriptmemiliki banyak tumpukan, tetapi memiliki "tumpukan" yang berperilaku lebih seperti tumpukan.
supercat
@supercat Itu semua masuk akal. Saya mendefinisikan ruang lingkup sebagai "bagian mana dari kode yang dapat mengakses variabel" (dan merasa ini adalah definisi paling standar) jadi saya pikir kami setuju :)
davec
Saya akan menganggap "ruang lingkup" suatu variabel sebagai dibatasi oleh waktu dan juga ruang. Variabel di lingkup objek-kelas diperlukan untuk menyimpan nilainya selama objek itu ada. Variabel dalam lingkup konteks eksekusi diperlukan untuk mempertahankan nilainya selama eksekusi tetap dalam konteks itu. Deklarasi variabel statis membuat pengidentifikasi yang cakupannya dibatasi ke blok saat ini, yang dilampirkan ke variabel yang cakupannya tidak terbatas.
supercat
@supercat Inilah sebabnya saya menggunakan kata seumur hidup, yang merupakan istilah yang saya sebut lingkup waktu. Ini mengurangi kebutuhan untuk membebani kata "lingkup" dengan begitu banyak makna. Sejauh yang saya tahu, tampaknya tidak ada konsensus total tentang definisi yang tepat, bahkan di antara sumber kanonik. Terminologi saya diambil sebagian dari K&R dan sebagian dari penggunaan yang berlaku di departemen CS pertama yang saya pelajari / ajarkan. Selalu baik untuk mendengar tampilan informasi lainnya.
davec
1
kamu pasti bercanda. dapatkah Anda benar-benar mendefinisikan variabel statis di dalam suatu fungsi?
Zaeem Sattar
168

Yang lain telah menjawab sapuan luas dengan cukup baik, jadi saya akan memberikan beberapa detail.

  1. Stack dan heap tidak harus tunggal. Situasi umum di mana Anda memiliki lebih dari satu tumpukan adalah jika Anda memiliki lebih dari satu utas dalam suatu proses. Dalam hal ini setiap utas memiliki tumpukan sendiri. Anda juga dapat memiliki lebih dari satu tumpukan, misalnya beberapa konfigurasi DLL dapat mengakibatkan alokasi DLL yang berbeda dari tumpukan yang berbeda, itulah sebabnya mengapa itu adalah ide yang buruk untuk melepaskan memori yang dialokasikan oleh perpustakaan yang berbeda.

  2. Dalam C Anda bisa mendapatkan manfaat dari alokasi panjang variabel melalui penggunaan alokasi , yang mengalokasikan pada tumpukan, sebagai lawan alokasi, yang mengalokasikan pada heap. Memori ini tidak akan bertahan dari pernyataan pengembalian Anda, tetapi ini berguna untuk buffer awal.

  3. Membuat buffer sementara yang besar di Windows yang tidak Anda gunakan tidak gratis. Ini karena kompiler akan menghasilkan loop probe stack yang dipanggil setiap kali fungsi Anda dimasukkan untuk memastikan stack ada (karena Windows menggunakan halaman penjaga tunggal di ujung tumpukan Anda untuk mendeteksi kapan perlu menumbuhkan tumpukan. Jika Anda mengakses memori lebih dari satu halaman dari ujung tumpukan Anda akan crash). Contoh:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}
Don Neufeld
sumber
Kembali "sebagai kebalikan dari alokasi": Apakah maksud Anda "sebagai lawan dari malloc"?
Peter Mortensen
Seberapa portabel alloca?
Peter Mortensen
@PeterMortensen itu bukan POSIX, portabilitas tidak dijamin.
Don Neufeld
135

Orang lain secara langsung menjawab pertanyaan Anda, tetapi ketika mencoba memahami tumpukan dan tumpukan, saya pikir akan membantu untuk mempertimbangkan tata letak memori dari proses UNIX tradisional (tanpa thread dan mmap()pengalokasi berbasis-basis). The Memory Management Glosarium halaman web memiliki diagram tata letak memori ini.

Tumpukan dan tumpukan secara tradisional terletak di ujung yang berlawanan dari ruang alamat virtual proses. Tumpukan tumbuh secara otomatis saat diakses, hingga ukuran yang ditentukan oleh kernel (yang dapat disesuaikan dengan setrlimit(RLIMIT_STACK, ...)). Tumpukan tumbuh ketika pengalokasi memori memanggil brk()atausbrk() system call, memetakan lebih banyak halaman memori fisik ke dalam ruang alamat virtual proses.

Dalam sistem tanpa memori virtual, seperti beberapa sistem tertanam, tata letak dasar yang sama sering berlaku, kecuali tumpukan dan tumpukan diperbaiki dalam ukuran. Namun, dalam sistem tertanam lainnya (seperti yang berbasis pada mikrokontroler PIC Microchip), tumpukan program adalah blok memori terpisah yang tidak dapat dialamatkan oleh instruksi perpindahan data, dan hanya dapat dimodifikasi atau dibaca secara tidak langsung melalui instruksi aliran program (panggilan, kembali, dll.). Arsitektur lain, seperti prosesor Intel Itanium, memiliki banyak tumpukan . Dalam pengertian ini, tumpukan adalah elemen arsitektur CPU.

bk1e
sumber
117

Apa itu tumpukan?

Tumpukan adalah tumpukan benda, biasanya yang tertata rapi.

Masukkan deskripsi gambar di sini

Tumpukan dalam arsitektur komputasi adalah wilayah memori tempat data ditambahkan atau dihapus dengan cara masuk terakhir keluar pertama.
Dalam aplikasi multi-utas, setiap utas akan memiliki tumpukan sendiri.

Apa itu heap?

Tumpukan adalah kumpulan benda-benda yang tertumpuk secara acak.

Masukkan deskripsi gambar di sini

Dalam arsitektur komputasi heap adalah area memori yang dialokasikan secara dinamis yang dikelola secara otomatis oleh sistem operasi atau perpustakaan manajer memori.
Memori pada heap dialokasikan, deallocated, dan diubah ukurannya secara teratur selama eksekusi program, dan ini dapat menyebabkan masalah yang disebut fragmentasi.
Fragmentasi terjadi ketika objek memori dialokasikan dengan ruang kecil di antaranya yang terlalu kecil untuk menampung objek memori tambahan.
Hasil bersih adalah persentase ruang tumpukan yang tidak dapat digunakan untuk alokasi memori lebih lanjut.

Berdua bersama

Dalam aplikasi multi-utas, setiap utas akan memiliki tumpukan sendiri. Tapi, semua utas yang berbeda akan berbagi tumpukan.
Karena utas yang berbeda berbagi tumpukan dalam aplikasi multi-utas, ini juga berarti bahwa harus ada koordinasi antara utas sehingga mereka tidak mencoba mengakses dan memanipulasi bagian memori yang sama dalam tumpukan di waktu yang sama.

Mana yang lebih cepat - tumpukan atau tumpukan? Dan mengapa?

Tumpukan jauh lebih cepat daripada tumpukan.
Ini karena cara memori dialokasikan pada stack.
Mengalokasikan memori pada stack adalah sesederhana memindahkan pointer stack ke atas.

Untuk orang yang baru dalam pemrograman, mungkin ide yang baik untuk menggunakan stack karena lebih mudah.
Karena tumpukannya kecil, Anda ingin menggunakannya ketika Anda tahu persis berapa banyak memori yang Anda perlukan untuk data Anda, atau jika Anda tahu ukuran data Anda sangat kecil.
Lebih baik menggunakan heap ketika Anda tahu bahwa Anda akan membutuhkan banyak memori untuk data Anda, atau Anda hanya tidak yakin berapa banyak memori yang akan Anda butuhkan (seperti dengan array dinamis).

Model Memori Java

Masukkan deskripsi gambar di sini

Tumpukan adalah area memori tempat variabel lokal (termasuk parameter metode) disimpan. Ketika datang ke variabel objek, ini hanyalah referensi (pointer) ke objek aktual di heap.
Setiap kali suatu objek dipakai, sepotong memori tumpukan disisihkan untuk menyimpan data (keadaan) objek itu. Karena objek dapat berisi objek lain, beberapa data ini sebenarnya bisa menyimpan referensi ke objek bersarang.

Shreyos Adikari
sumber
115

Tumpukan adalah bagian dari memori yang dapat dimanipulasi melalui beberapa instruksi bahasa rakitan kunci, seperti 'pop' (menghapus dan mengembalikan nilai dari tumpukan) dan 'mendorong' (mendorong nilai ke tumpukan), tetapi juga memanggil ( panggil subrutin - ini mendorong alamat untuk kembali ke tumpukan) dan kembali (kembali dari subrutin - ini memunculkan alamat dari tumpukan dan melompat ke sana). Ini adalah wilayah memori di bawah register penunjuk tumpukan, yang dapat diatur sesuai kebutuhan. Tumpukan juga digunakan untuk meneruskan argumen ke subrutin, dan juga untuk menjaga nilai dalam register sebelum memanggil subrutin.

Tumpukan adalah sebagian dari memori yang diberikan kepada aplikasi oleh sistem operasi, biasanya melalui syscall seperti malloc. Pada OS modern, memori ini adalah sekumpulan halaman yang hanya dapat diakses oleh proses panggilan.

Ukuran tumpukan ditentukan saat runtime, dan umumnya tidak tumbuh setelah program diluncurkan. Dalam program C, tumpukan harus cukup besar untuk menampung setiap variabel yang dideklarasikan dalam setiap fungsi. Tumpukan akan tumbuh secara dinamis sesuai kebutuhan, tetapi OS akhirnya membuat panggilan (sering akan menumpuk lebih dari nilai yang diminta oleh malloc, sehingga setidaknya beberapa mallocs di masa depan tidak perlu kembali ke kernel untuk dapatkan lebih banyak memori. Perilaku ini sering kali dapat disesuaikan)

Karena Anda telah mengalokasikan stack sebelum meluncurkan program, Anda tidak perlu melakukan malloc sebelum Anda dapat menggunakan stack, jadi itu sedikit keuntungan di sana. Dalam praktiknya, sangat sulit untuk memprediksi apa yang akan cepat dan apa yang akan lambat dalam sistem operasi modern yang memiliki subsistem memori virtual, karena bagaimana halaman diimplementasikan dan di mana mereka disimpan adalah detail implementasi.

Daniel Papasian
sumber
2
Juga patut disebutkan di sini bahwa intel sangat mengoptimalkan akses stack, terutama hal-hal seperti memprediksi di mana Anda kembali dari suatu fungsi.
Tom Leys
113

Saya pikir banyak orang telah memberi Anda jawaban yang benar tentang masalah ini.

Namun, satu detail yang terlewatkan adalah "tumpukan" itu sebenarnya mungkin disebut "toko gratis". Alasan untuk perbedaan ini adalah bahwa toko bebas asli diimplementasikan dengan struktur data yang dikenal sebagai "tumpukan binomial." Karena alasan itu, pengalokasian dari implementasi awal malloc () / free () adalah alokasi dari heap. Namun, di zaman modern ini, sebagian besar toko gratis diimplementasikan dengan struktur data yang sangat rumit yang bukan tumpukan binomial.


sumber
8
Nitpick lain - sebagian besar jawaban (ringan) menyiratkan bahwa penggunaan "tumpukan" diperlukan oleh Cbahasa. Ini adalah kesalahpahaman umum, meskipun itu adalah (sejauh) mendominasi paradigma untuk implementasi C99 6.2.4 automatic storage duration objects(variabel). Bahkan, kata "tumpukan" bahkan tidak muncul dalam C99standar bahasa: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
johne
[@Heath] Saya punya sedikit komentar atas jawaban Anda. Lihatlah jawaban yang diterima untuk pertanyaan ini . Dikatakan bahwa toko gratis kemungkinan besar sama dengan heap , meskipun tidak harus demikian.
OmarOthman
91

Anda dapat melakukan beberapa hal menarik dengan tumpukan. Misalnya, Anda memiliki fungsi seperti Alokasi (dengan asumsi Anda bisa melewati peringatan berlebihan tentang penggunaannya), yang merupakan bentuk malloc yang secara khusus menggunakan stack, bukan heap, untuk memori.

Yang mengatakan, kesalahan memori berbasis stack adalah beberapa yang terburuk yang pernah saya alami. Jika Anda menggunakan memori tumpukan, dan Anda melampaui batas blok yang dialokasikan, Anda memiliki peluang yang layak untuk memicu kesalahan segmen. (Tidak 100%: blok Anda mungkin berdekatan dengan yang lain yang sebelumnya Anda alokasikan). Tetapi karena variabel yang dibuat pada tumpukan selalu berdekatan satu sama lain, penulisan di luar batas dapat mengubah nilai variabel lain. Saya telah belajar bahwa setiap kali saya merasa bahwa program saya telah berhenti mematuhi hukum logika, itu mungkin buffer overflow.

Peter
sumber
Seberapa portabel alloca? Misalnya, apakah ini berfungsi pada Windows? Apakah hanya untuk sistem operasi mirip Unix?
Peter Mortensen
89

Secara sederhana, stack adalah tempat variabel lokal dibuat. Juga, setiap kali Anda memanggil subrutin penghitung program (penunjuk ke instruksi mesin berikutnya) dan register penting apa pun, dan terkadang parameter didorong pada tumpukan. Kemudian setiap variabel lokal di dalam subrutin didorong ke stack (dan digunakan dari sana). Ketika subrutin selesai, semua itu akan muncul kembali dari tumpukan. PC dan mendaftar data didapat dan dimasukkan kembali ke tempatnya semula, sehingga program Anda dapat berjalan dengan cara yang menyenangkan.

Heap adalah area alokasi memori dinamis yang terbuat dari memori (panggilan "baru" atau "alokasikan" eksplisit). Ini adalah struktur data khusus yang dapat melacak blok memori dengan berbagai ukuran dan status alokasi mereka.

Dalam sistem "klasik", RAM diletakkan sedemikian rupa sehingga penumpukan tumpukan dimulai di bagian bawah memori, penumpukan tumpukan dimulai di bagian atas, dan mereka tumbuh saling berhadapan. Jika tumpang tindih, Anda kehabisan RAM. Itu tidak bekerja dengan OS multi-threaded modern sekalipun. Setiap utas harus memiliki tumpukan sendiri, dan itu dapat dibuat secara dinamis.

TED
sumber
[@TED] Mengapa Anda mengatakan "terkadang parameter didorong pada stack"? Yang saya tahu adalah mereka selalu begitu . Bisakah Anda menjelaskan lebih lanjut?
OmarOthman
1
@OmarOthman - Saya katakan itu karena sepenuhnya tergantung pada penulis kompiler / juru bahasa Anda apa yang terjadi ketika subrutin dipanggil. Perilaku Fortran klasik adalah tidak menggunakan tumpukan sama sekali. Beberapa bahasa mendukung hal-hal eksotis seperti pass-by-name, yang secara efektif merupakan pengganti teks.
TED
83

Dari WikiAnwser.

Tumpukan

Ketika suatu fungsi atau metode memanggil fungsi lain yang pada gilirannya memanggil fungsi lain, dll., Pelaksanaan semua fungsi tersebut tetap ditunda sampai fungsi terakhir mengembalikan nilainya.

Rantai panggilan fungsi yang ditangguhkan ini adalah tumpukan, karena elemen-elemen dalam tumpukan (panggilan fungsi) saling bergantung.

Tumpukan penting untuk dipertimbangkan dalam penanganan pengecualian dan eksekusi ulir.

Tumpukan

Heap hanyalah memori yang digunakan oleh program untuk menyimpan variabel. Elemen heap (variabel) tidak memiliki ketergantungan satu sama lain dan selalu dapat diakses secara acak kapan saja.

devXen
sumber
"Aku suka jawaban yang diterima dengan lebih baik karena levelnya bahkan lebih rendah." Itu hal yang buruk, bukan hal yang baik.
Lightness Races dalam Orbit
54

Tumpukan

  • Akses sangat cepat
  • Tidak harus secara eksplisit mendelegasikan variabel
  • Ruang dikelola secara efisien oleh CPU, memori tidak akan menjadi terfragmentasi
  • Hanya variabel lokal
  • Batas ukuran tumpukan (bergantung pada OS)
  • Variabel tidak dapat diubah ukurannya

Tumpukan

  • Variabel dapat diakses secara global
  • Tidak ada batasan ukuran memori
  • (Relatif) akses lebih lambat
  • Tidak ada jaminan penggunaan ruang yang efisien, memori dapat menjadi terfragmentasi dari waktu ke waktu karena blok memori dialokasikan, lalu dibebaskan
  • Anda harus mengelola memori (Anda bertanggung jawab untuk mengalokasikan dan membebaskan variabel)
  • Variabel dapat diubah ukurannya menggunakan realloc ()
tidak diketahui
sumber
50

OK, sederhana dan dalam kata-kata singkat, itu berarti dipesan dan tidak dipesan ...!

Stack : Dalam tumpukan item, segalanya saling terkait, artinya akan lebih cepat dan lebih efisien untuk diproses! ...

Jadi selalu ada indeks untuk menunjuk item tertentu, juga pemrosesan akan lebih cepat, ada hubungan antara item juga! ...

Heap : Tidak ada pesanan, pemrosesan akan lebih lambat dan nilai-nilai kacau bersama tanpa urutan atau indeks tertentu ... ada acak dan tidak ada hubungan di antara mereka ... sehingga waktu eksekusi dan penggunaan dapat bervariasi ...

Saya juga membuat gambar di bawah ini untuk menunjukkan bagaimana tampilannya:

masukkan deskripsi gambar di sini

Alireza
sumber
49

Pendeknya

Tumpukan digunakan untuk alokasi memori statis dan tumpukan untuk alokasi memori dinamis, keduanya disimpan dalam RAM komputer.


Secara terperinci

Tumpukan

Tumpukan adalah struktur data "LIFO" (last in, first out), yang dikelola dan dioptimalkan oleh CPU dengan cukup dekat. Setiap kali suatu fungsi mendeklarasikan variabel baru, ia "didorong" ke stack. Kemudian setiap kali suatu fungsi keluar, semua variabel yang didorong ke stack oleh fungsi itu, dibebaskan (artinya, mereka dihapus). Setelah variabel tumpukan dibebaskan, wilayah memori tersebut menjadi tersedia untuk variabel tumpukan lainnya.

Keuntungan menggunakan stack untuk menyimpan variabel, adalah memori dikelola untuk Anda. Anda tidak perlu mengalokasikan memori dengan tangan, atau membebaskannya setelah Anda tidak membutuhkannya lagi. Terlebih lagi, karena CPU mengatur memori tumpukan dengan sangat efisien, membaca dari dan menulis ke variabel tumpukan sangat cepat.

Lebih banyak dapat ditemukan di sini .


Heap

Tumpukan adalah wilayah memori komputer Anda yang tidak dikelola secara otomatis untuk Anda, dan tidak begitu ketat dikelola oleh CPU. Ini adalah wilayah memori yang lebih bebas mengambang (dan lebih besar). Untuk mengalokasikan memori pada heap, Anda harus menggunakan malloc () atau calloc (), yang merupakan fungsi C bawaan. Setelah Anda mengalokasikan memori pada heap, Anda bertanggung jawab untuk menggunakan free () untuk membatalkan alokasi memori setelah Anda tidak membutuhkannya lagi.

Jika Anda gagal melakukan ini, program Anda akan memiliki apa yang dikenal sebagai kebocoran memori. Artinya, memori pada heap masih akan disisihkan (dan tidak akan tersedia untuk proses lain). Seperti yang akan kita lihat di bagian debugging, ada alat bernama Valgrind yang dapat membantu Anda mendeteksi kebocoran memori.

Berbeda dengan stack, heap tidak memiliki batasan ukuran pada ukuran variabel (terlepas dari batasan fisik komputer Anda). Memori tumpukan sedikit lebih lambat untuk dibaca dan ditulis, karena kita harus menggunakan pointer untuk mengakses memori pada tumpukan. Kami akan segera berbicara tentang petunjuk.

Berbeda dengan stack, variabel yang dibuat di heap dapat diakses oleh fungsi apa saja, di mana saja di program Anda. Variabel tumpukan pada dasarnya bersifat global.

Lebih banyak dapat ditemukan di sini .


Variabel yang dialokasikan pada tumpukan disimpan langsung ke memori dan akses ke memori ini sangat cepat, dan alokasi tersebut ditangani ketika program dikompilasi. Ketika suatu fungsi atau metode memanggil fungsi lain yang pada gilirannya memanggil fungsi lain, dll., Pelaksanaan semua fungsi tersebut tetap ditunda sampai fungsi terakhir mengembalikan nilainya. Tumpukan selalu dicadangkan dalam urutan LIFO, blok yang paling terakhir dipesan selalu blok berikutnya yang akan dibebaskan. Ini membuatnya sangat sederhana untuk melacak tumpukan, membebaskan blok dari tumpukan tidak lebih dari menyesuaikan satu pointer.

Variabel yang dialokasikan pada heap memiliki alokasi memori pada saat run time dan mengakses memori ini sedikit lebih lambat, tetapi ukuran heap hanya dibatasi oleh ukuran memori virtual. Elemen tumpukan tidak memiliki ketergantungan satu sama lain dan selalu dapat diakses secara acak kapan saja. Anda dapat mengalokasikan blok kapan saja dan membebaskannya kapan saja. Ini membuatnya jauh lebih kompleks untuk melacak bagian mana dari tumpukan yang dialokasikan atau gratis pada waktu tertentu.

Masukkan deskripsi gambar di sini

Anda dapat menggunakan tumpukan jika Anda tahu persis berapa banyak data yang perlu Anda alokasikan sebelum waktu kompilasi, dan itu tidak terlalu besar. Anda dapat menggunakan tumpukan jika Anda tidak tahu persis berapa banyak data yang akan Anda butuhkan saat runtime atau jika Anda perlu mengalokasikan banyak data.

Dalam situasi multi-utas, masing-masing utas akan memiliki tumpukan mandiri sepenuhnya, tetapi mereka akan berbagi tumpukan. Tumpukan adalah utas khusus dan tumpukan adalah spesifik aplikasi. Tumpukan penting untuk dipertimbangkan dalam penanganan pengecualian dan eksekusi ulir.

Setiap utas mendapat tumpukan, sementara biasanya hanya ada satu tumpukan untuk aplikasi (meskipun tidak jarang memiliki banyak tumpukan untuk berbagai jenis alokasi).

Masukkan deskripsi gambar di sini

Pada saat run-time, jika aplikasi membutuhkan lebih banyak tumpukan, ia dapat mengalokasikan memori dari memori bebas dan jika stack membutuhkan memori, ia dapat mengalokasikan memori dari memori yang dialokasikan memori bebas untuk aplikasi tersebut.

Bahkan, lebih banyak detail diberikan di sini dan di sini .


Sekarang datang ke jawaban pertanyaan Anda .

Sejauh mana mereka dikendalikan oleh runtime OS atau bahasa?

OS mengalokasikan tumpukan untuk setiap utas tingkat sistem saat utas dibuat. Biasanya OS dipanggil oleh runtime bahasa untuk mengalokasikan heap untuk aplikasi.

Lebih banyak dapat ditemukan di sini .

Apa ruang lingkup mereka?

Sudah diberikan di atas.

"Anda dapat menggunakan tumpukan jika Anda tahu persis berapa banyak data yang perlu Anda alokasikan sebelum waktu kompilasi, dan itu tidak terlalu besar. Anda dapat menggunakan tumpukan jika Anda tidak tahu persis berapa banyak data yang akan Anda butuhkan saat runtime atau jika Anda perlu mengalokasikan banyak data. "

Lebih banyak dapat ditemukan di sini .

Apa yang menentukan ukuran masing-masing?

Ukuran tumpukan diatur oleh OS saat utas dibuat. Ukuran heap diatur pada startup aplikasi, tetapi bisa bertambah sesuai ruang yang dibutuhkan (pengalokasi meminta lebih banyak memori dari sistem operasi).

Apa yang membuat seseorang lebih cepat?

Alokasi tumpukan jauh lebih cepat karena yang dilakukannya hanyalah memindahkan penunjuk tumpukan. Menggunakan kumpulan memori, Anda bisa mendapatkan kinerja yang sebanding dari alokasi tumpukan, tetapi itu datang dengan sedikit kompleksitas tambahan dan sakit kepala sendiri.

Juga, tumpukan vs tumpukan tidak hanya pertimbangan kinerja; itu juga memberi tahu Anda banyak tentang umur objek yang diharapkan.

Detail dapat ditemukan dari sini .

Abrar Jahin
sumber
36

Pada 1980-an, UNIX berkembang biak seperti kelinci dengan perusahaan besar menggulirkannya sendiri. Exxon memiliki satu seperti puluhan nama merek yang hilang karena sejarah. Bagaimana memori diletakkan adalah atas kebijaksanaan banyak pelaksana.

Sebuah program C yang umum diletakkan dalam memori datar dengan peluang untuk meningkat dengan mengubah nilai brk (). Biasanya, HEAP tepat di bawah nilai brk ini dan meningkatkan brk meningkatkan jumlah tumpukan yang tersedia.

STACK tunggal biasanya merupakan area di bawah HEAP yang merupakan saluran memori yang tidak mengandung nilai apa pun sampai bagian atas blok memori tetap berikutnya. Blok berikut ini seringkali adalah KODE yang dapat ditimpa oleh tumpukan data di salah satu peretasan terkenal di masanya.

Satu blok memori yang khas adalah BSS (blok bernilai nol) yang secara tidak sengaja tidak memusatkan perhatian pada penawaran satu produsen. Lainnya adalah DATA yang berisi nilai yang diinisialisasi, termasuk string dan angka. Yang ketiga adalah CODE yang mengandung CRT (C runtime), utama, fungsi, dan perpustakaan.

Munculnya memori virtual di UNIX mengubah banyak kendala. Tidak ada alasan obyektif mengapa blok-blok ini perlu bersebelahan, atau tetap dalam ukuran, atau dipesan dengan cara tertentu sekarang. Tentu saja, sebelum UNIX adalah Multics yang tidak menderita kendala ini. Berikut adalah skema yang menunjukkan salah satu tata letak memori era itu.

Tata letak memori program UNIX C gaya 1980-an

Jlettvin
sumber
36

tumpukan , tumpukan dan data dari setiap proses dalam memori virtual:

tumpukan, tumpukan dan data statis

Yousha Aleayoub
sumber
26

Beberapa sen: Saya pikir, akan lebih baik untuk menggambar memori grafis dan lebih sederhana:

Ini adalah visi saya tentang proses pembangunan memori dengan penyederhanaan untuk lebih mudah memahami apa yang terjadi


Panah - menunjukkan tempat tumbuhnya tumpukan dan tumpukan, proses ukuran tumpukan memiliki batas, ditentukan dalam OS, batas ukuran tumpukan benang dengan parameter dalam pembuatan API biasanya. Heap biasanya dibatasi oleh proses ukuran memori virtual maksimum, misalnya 32 bit 2-4 GB.

Jadi cara sederhana: tumpukan proses adalah umum untuk proses dan semua utas di dalamnya, gunakan untuk alokasi memori dalam kasus umum dengan sesuatu seperti malloc () .

Stack adalah memori cepat untuk menyimpan dalam fungsi umum pointer dan variabel pengembalian, diproses sebagai parameter dalam panggilan fungsi, variabel fungsi lokal.

Maxim Akristiniy
sumber
23

Karena beberapa jawaban menjadi rewel, saya akan berkontribusi tungau saya.

Anehnya, tidak ada yang menyebutkan bahwa banyak (mis. Tidak terkait dengan jumlah menjalankan thread tingkat OS) tumpukan panggilan dapat ditemukan tidak hanya dalam bahasa eksotis (PostScript) atau platform (Intel Itanium), tetapi juga dalam serat , benang hijau dan beberapa implementasi coroutine .

Serat, benang hijau, dan coroutine dalam banyak hal serupa, yang menyebabkan banyak kebingungan. Perbedaan antara serat dan benang hijau adalah bahwa yang pertama menggunakan multitasking kooperatif, sedangkan yang kedua mungkin memiliki fitur kooperatif atau preemptive (atau bahkan keduanya). Untuk perbedaan antara serat dan coroutine, lihat di sini .

Bagaimanapun, tujuan dari kedua serat, benang hijau dan coroutine adalah memiliki beberapa fungsi yang dijalankan secara bersamaan, tetapi tidak secara paralel (lihat pertanyaan SO untuk perbedaan ini) dalam satu thread level OS tunggal, mentransfer kontrol bolak-balik satu sama lain secara terorganisir.

Saat menggunakan serat, benang hijau atau coroutine, Anda biasanya memiliki tumpukan terpisah per fungsi. (Secara teknis, bukan hanya setumpuk tetapi seluruh konteks eksekusi adalah per fungsi. Yang paling penting, register CPU.) Untuk setiap utas ada tumpukan yang sama banyaknya dengan fungsi yang sedang berjalan secara bersamaan, dan utas tersebut beralih antara menjalankan setiap fungsi sesuai dengan logika program Anda. Ketika suatu fungsi berjalan ke ujungnya, tumpukannya dihancurkan. Jadi, jumlah dan masa hidup tumpukan adalah dinamis dan tidak ditentukan oleh jumlah utas tingkat OS!

Perhatikan bahwa saya berkata " biasanya memiliki stack per fungsi yang terpisah". Ada implementasi stackoutine stackless dan stackless. Paling penting stackful C ++ implementasi yang Boost.Coroutine dan Microsoft PPL 's . (Namun, fungsi resume C ++ (alias " dan "), yang diusulkan ke C ++ 17, kemungkinan akan menggunakan coroutine stackless.)async/awaitasyncawait

Proposal serat ke pustaka standar C ++ akan hadir. Juga, ada beberapa perpustakaan pihak ketiga . Utas hijau sangat populer dalam bahasa seperti Python dan Ruby.

shakurov
sumber
19

Saya memiliki sesuatu untuk dibagikan, meskipun poin utama sudah dibahas.

Tumpukan

  • Akses sangat cepat.
  • Disimpan dalam RAM.
  • Panggilan fungsi dimuat di sini bersama dengan variabel lokal dan parameter fungsi yang diteruskan.
  • Ruang dibebaskan secara otomatis ketika program keluar dari cakupan.
  • Disimpan dalam memori sekuensial.

Tumpukan

  • Akses lambat dibandingkan dengan Stack.
  • Disimpan dalam RAM.
  • Variabel yang dibuat secara dinamis disimpan di sini, yang nantinya membutuhkan pembebasan memori yang dialokasikan setelah digunakan.
  • Disimpan di mana pun alokasi memori dilakukan, selalu diakses oleh pointer.

Catatan menarik:

  • Jika pemanggilan fungsi disimpan dalam heap, itu akan menghasilkan 2 poin berantakan:
    1. Karena penyimpanan berurutan dalam tumpukan, eksekusi lebih cepat. Penyimpanan dalam tumpukan akan menghasilkan konsumsi waktu yang sangat besar sehingga membuat seluruh program berjalan lebih lambat.
    2. Jika fungsi disimpan di heap (penyimpanan berantakan menunjuk oleh pointer), tidak akan ada cara untuk kembali ke alamat pemanggil kembali (yang tumpukan memberikan karena penyimpanan berurutan dalam memori).
Pankaj Kumar Thapa
sumber
1
ringkas dan bersih. bagus :)
ingconti
13

Wow! Begitu banyak jawaban dan saya rasa salah satu dari mereka tidak benar ...

1) Di mana dan apa mereka (secara fisik dalam memori komputer nyata)?

Tumpukan adalah memori yang dimulai sebagai alamat memori tertinggi yang dialokasikan untuk gambar program Anda, dan kemudian berkurang nilainya dari sana. Ini dicadangkan untuk parameter fungsi yang disebut dan untuk semua variabel sementara yang digunakan dalam fungsi.

Ada dua tumpukan: publik dan pribadi.

Tumpukan pribadi dimulai pada batas 16-byte (untuk program 64-bit) atau batas 8-byte (untuk program 32-bit) setelah kode byte terakhir dalam program Anda, dan kemudian meningkat nilainya dari sana. Ini juga disebut heap default.

Jika tumpukan pribadi menjadi terlalu besar itu akan tumpang tindih area tumpukan, seperti tumpukan akan tumpang tindih tumpukan jika terlalu besar. Karena tumpukan dimulai dari alamat yang lebih tinggi dan berjalan turun ke alamat yang lebih rendah, dengan peretasan yang tepat Anda bisa membuat tumpukan itu begitu besar sehingga akan membanjiri area tumpukan pribadi dan menumpuk area kode. Triknya adalah tumpang tindih dengan area kode yang dapat Anda kaitkan ke dalam kode. Agak sulit untuk dilakukan dan Anda berisiko crash program, tetapi mudah dan sangat efektif.

Tumpukan publik berada di ruang memori itu sendiri di luar ruang gambar program Anda. Memori inilah yang akan disedot ke hard disk jika sumber daya memori menjadi langka.

2) Sejauh mana mereka dikendalikan oleh runtime OS atau bahasa?

Tumpukan dikendalikan oleh programmer, tumpukan pribadi dikelola oleh OS, dan tumpukan publik tidak dikendalikan oleh siapa pun karena ini adalah layanan OS - Anda membuat permintaan dan apakah itu diberikan atau ditolak.

2b) Apa cakupannya?

Semuanya bersifat global untuk program ini, tetapi isinya dapat bersifat pribadi, publik, atau global.

2c) Apa yang menentukan ukuran masing-masing?

Ukuran tumpukan dan tumpukan pribadi ditentukan oleh opsi runtime kompiler Anda. Tumpukan publik diinisialisasi pada saat runtime menggunakan parameter ukuran.

2d) Apa yang membuatnya lebih cepat?

Mereka tidak dirancang untuk menjadi cepat, mereka dirancang untuk berguna. Bagaimana programmer menggunakannya menentukan apakah mereka "cepat" atau "lambat"

REF:

https://norasandler.com/2019/02/18/Write-a-Compiler-10.html

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate

ar18
sumber
8

Banyak jawaban yang benar sebagai konsep, tetapi kita harus mencatat bahwa setumpuk diperlukan oleh perangkat keras (yaitu mikroprosesor) untuk memungkinkan memanggil subrutin (CALL dalam bahasa assembly ..). (OOP guys akan menyebutnya metode )

Pada tumpukan Anda menyimpan alamat kembali dan panggilan → push / ret → pop dikelola langsung di perangkat keras.

Anda dapat menggunakan tumpukan untuk melewatkan parameter .. bahkan jika itu lebih lambat daripada menggunakan register (akankah seorang guru mikroprosesor mengatakan atau buku BIOS 1980-an yang bagus ...)

  • Tanpa tumpukan, mikroprosesor tidak dapat bekerja. (kami tidak dapat membayangkan program, bahkan dalam bahasa assembly, tanpa subrutin / fungsi)
  • Tanpa tumpukan itu bisa. (Program bahasa assembly dapat bekerja tanpa, karena heap adalah konsep OS, sebagai malloc, itu adalah panggilan OS / Lib.

Penggunaan tumpukan lebih cepat karena:

  • Apakah perangkat keras, dan bahkan push / pop sangat efisien.
  • malloc mengharuskan memasuki mode kernel, menggunakan kunci / semaphore (atau primitif sinkronisasi lainnya) mengeksekusi beberapa kode dan mengelola beberapa struktur yang diperlukan untuk melacak alokasi.
ingconti
sumber
Apa itu OPP? Apakah maksud Anda OOP ( object-oriented_programming )?
Peter Mortensen
Apakah Anda bermaksud mengatakan bahwa itu mallocadalah panggilan kernel?
Peter Mortensen
1) ya, maaf .. OOP ... 2) malloc: Saya menulis sesaat, maaf ... malloc ada di ruang pengguna .. tapi bisa memicu panggilan lain .... intinya adalah bahwa menggunakan heap BISA menjadi sangat lambat ...
ingconti
" Banyak jawaban yang benar sebagai konsep, tetapi kita harus mencatat bahwa setumpuk diperlukan oleh perangkat keras (yaitu mikroprosesor) untuk memungkinkan pemanggilan subrutin (PANGGILAN dalam bahasa rakitan ..) ". Anda membingungkan tumpukan CPU (jika ada satu di CPU modern) dan tumpukan runtime bahasa (satu per utas). Ketika programmer berbicara tentang stack, ini adalah stack eksekusi thread dari runtime, misalnya stack thread NET), kita tidak berbicara tentang stack CPU.
mnt
1

Tumpukan ini pada dasarnya adalah memori yang mudah diakses yang hanya mengelola barang-barangnya sebagai tumpukan yang baik. Hanya item yang ukurannya diketahui sebelumnya yang bisa masuk ke tumpukan . Ini adalah kasus untuk angka, string, boolean.

The tumpukan adalah memori untuk item yang Anda tidak bisa mentakdirkan ukuran yang tepat dan struktur . Karena objek dan array dapat dimutasi dan diubah saat runtime, mereka harus masuk ke heap.

Sumber: Academind

NattyC
sumber
0

Terima kasih atas diskusi yang sangat baik tetapi sebagai noob nyata saya bertanya-tanya di mana instruksi disimpan? Dalam AWAL para ilmuwan memutuskan antara dua arsitektur (von NEUMANN di mana semuanya dianggap DATA dan HARVARD di mana area memori dicadangkan untuk instruksi dan yang lainnya untuk data). Pada akhirnya, kami menggunakan desain von Neumann dan sekarang semuanya dianggap 'sama'. Ini menyulitkan saya ketika saya sedang belajar perakitan https://www.cs.virginia.edu/~evans/cs216/guides/x86.html karena mereka berbicara tentang register dan penunjuk tumpukan.

Segala sesuatu di atas berbicara tentang DATA. Dugaan saya adalah bahwa karena instruksi adalah sesuatu yang didefinisikan dengan jejak memori tertentu, itu akan masuk ke tumpukan dan jadi semua register 'yang' dibahas dalam perakitan ada di tumpukan. Tentu saja kemudian datang pemrograman berorientasi objek dengan instruksi dan data yang muncul ke dalam struktur yang dinamis jadi sekarang instruksi akan disimpan di heap juga?

aquagremlin
sumber