Stack and Heap memory in Java


99

Seperti yang saya mengerti, di Jawa, memori stack menampung primitif dan metode doa dan memori tumpukan digunakan untuk menyimpan objek.

Misalkan saya punya kelas

class A {
       int a ;
       String b;
       //getters and setters
}
  1. Di mana primitif adi kelas Aakan disimpan?

  2. Mengapa tumpukan memori ada sama sekali? Mengapa kita tidak bisa menyimpan semuanya di stack?

  3. Ketika objek dikumpulkan, apakah tumpukan yang terkait dengan keberatan dihancurkan?



@ S.Lott, kecuali yang ini tentang Jawa, bukan C.
Péter Török

@ Péter Török: Setuju. Sementara sampel kode adalah Java, tidak ada tag untuk menunjukkan bahwa itu hanya Java. Dan prinsip umum harus berlaku juga untuk Java sebagai C. Selanjutnya, ada banyak jawaban untuk pertanyaan ini pada Stack Overflow.
S.Lott

9
@SteveHaigh: di situs ini, semua orang terlalu khawatir tentang apakah sesuatu termasuk di sini ... Saya ingin tahu apa yang mindshare situs ini benar-benar dapatkan dengan semua rewel tentang apakah pertanyaan ada di sini atau tidak.
Sam Goldberg

Jawaban:


106

Perbedaan mendasar antara tumpukan dan tumpukan adalah siklus hidup dari nilai-nilai.

Nilai tumpukan hanya ada dalam lingkup fungsi tempat mereka dibuat. Setelah kembali, nilai tersebut dibuang.
Namun nilai heap ada di heap. Mereka dibuat di beberapa titik waktu, dan dirusak di tempat lain (baik oleh GC atau secara manual, tergantung pada bahasa / runtime).

Sekarang Java hanya menyimpan primitif di stack. Ini menjaga stack tetap kecil dan membantu menjaga frame stack individual tetap kecil, sehingga memungkinkan lebih banyak panggilan bersarang.
Objek dibuat di heap, dan hanya referensi (yang pada gilirannya adalah primitif) dilewatkan di tumpukan.

Jadi jika Anda membuat objek, ia diletakkan di heap, dengan semua variabel yang menjadi miliknya, sehingga bisa bertahan setelah fungsi panggil kembali.


2
"dan hanya referensi (yang pada gilirannya adalah primitif)" Mengapa mengatakan bahwa referensi adalah primitif? Bisakah Anda mengklarifikasi?
Geek

5
@ Geek: Karena definisi umum tipe data primitif berlaku: "tipe data yang disediakan oleh bahasa pemrograman sebagai blok penyusun dasar" . Anda mungkin juga memperhatikan bahwa rujukan dicantumkan di antara contoh kanonik lebih lanjut dalam artikel .
back2dos

4
@ Geek: Dalam hal data, Anda dapat melihat salah satu tipe data primitif - termasuk referensi - sebagai angka. Bahkan chars adalah angka dan dapat digunakan secara bergantian. Referensi juga hanya nomor yang merujuk ke alamat memori, panjangnya 32 atau 64 bit (meskipun tidak dapat digunakan seperti itu - kecuali jika Anda mengacaukannya sun.misc.Unsafe).
Sune Rasmussen

3
Terminologi Jawaban ini salah. Menurut Spesifikasi Bahasa Jawa, referensi BUKAN primitif. Inti dari apa yang dikatakan Jawabannya adalah benar. (Meskipun Anda dapat membuat argumen bahwa referensi yang "dalam arti" primitif adalah dengan dengan The JLS mendefinisikan terminologi Jawa, dan mengatakan bahwa tipe primitif yang. boolean, byte, short, char, int, long, floatDan double.)
Stephen C

4
Seperti yang saya temukan di artikel ini , Java dapat menyimpan objek di stack (atau bahkan di register untuk objek berumur pendek). JVM dapat melakukan sedikit di bawah selimut. Tidak sepenuhnya benar untuk mengatakan "Sekarang Java hanya menyimpan primitif di stack."

49

Di mana bidang primitif disimpan?

Bidang primitif disimpan sebagai bagian dari objek yang dipakai di suatu tempat . Cara termudah untuk memikirkan di mana ini - adalah tumpukan. Namun , ini tidak selalu terjadi. Seperti yang dijelaskan dalam teori dan praktik Java: Legenda kinerja perkotaan, ditinjau kembali :

JVM dapat menggunakan teknik yang disebut analisis melarikan diri, yang dengannya mereka dapat mengatakan bahwa objek tertentu tetap terbatas pada satu utas untuk seluruh masa pakainya, dan masa itu dibatasi oleh masa pakai kerangka tumpukan yang diberikan. Benda-benda seperti itu dapat dengan aman dialokasikan pada tumpukan, bukan tumpukan. Bahkan lebih baik, untuk objek kecil, JVM dapat mengoptimalkan alokasi sepenuhnya dan hanya mengangkat bidang objek ke register.

Jadi, melampaui mengatakan "objek dibuat dan bidang ada di sana juga", orang tidak dapat mengatakan apakah ada sesuatu di tumpukan atau di tumpukan. Perhatikan bahwa untuk objek kecil, berumur pendek, kemungkinan bahwa 'objek' tidak akan ada dalam memori seperti itu dan sebagai gantinya dapat menempatkan bidangnya langsung di register.

Makalah ini diakhiri dengan:

Secara mengejutkan, JVM pandai mencari tahu hal-hal yang kami anggap hanya pengembang yang tahu. Dengan membiarkan JVM memilih antara alokasi tumpukan dan alokasi tumpukan berdasarkan kasus per kasus, kita bisa mendapatkan manfaat kinerja dari alokasi tumpukan tanpa membuat programmer merasa tersinggung apakah akan mengalokasikan pada tumpukan atau pada tumpukan.

Jadi, jika Anda memiliki kode yang terlihat seperti:

void foo(int arg) {
    Bar qux = new Bar(arg);
    ...
}

di mana yang ...tidak memungkinkan quxuntuk meninggalkan ruang lingkup itu, qux dapat dialokasikan pada tumpukan sebagai gantinya. Ini sebenarnya adalah kemenangan untuk VM karena itu berarti tidak perlu menjadi sampah yang dikumpulkan - itu akan hilang ketika meninggalkan ruang lingkup.

Lebih lanjut tentang analisis pelarian di Wikipedia. Bagi mereka yang ingin mempelajari makalah, Escape Analysis for Java dari IBM. Bagi mereka yang berasal dari dunia C #, Anda mungkin menemukan The Stack Is an Detail Implementasi dan The Truth About Value Type oleh Eric Lippert bacaan yang baik (mereka berguna untuk tipe Java juga karena banyak konsep dan aspek yang sama atau mirip) . Mengapa .Net buku berbicara tentang alokasi memori tumpukan vs tumpukan? juga masuk ke ini.

Di mengapa tumpukan dan tumpukan

Di atas tumpukan

Jadi, mengapa memiliki tumpukan atau tumpukan sama sekali? Untuk hal-hal yang meninggalkan ruang lingkup, tumpukan bisa mahal. Pertimbangkan kodenya:

void foo(String arg) {
    bar(arg);
    ...
}

void bar(String arg) {
    qux(arg);
    ...
}

void qux(String arg) {
    ...
}

Parameter juga merupakan bagian dari tumpukan. Dalam situasi yang Anda tidak memiliki tumpukan, Anda akan melewati set nilai penuh pada tumpukan. Ini bagus untuk "foo"dan string kecil ... tapi apa yang akan terjadi jika seseorang meletakkan file XML besar di string itu. Setiap panggilan akan menyalin seluruh string besar ke tumpukan - dan itu akan sangat sia-sia.

Sebaliknya, lebih baik untuk meletakkan benda-benda yang memiliki kehidupan di luar lingkup langsung (diteruskan ke lingkup lain, terjebak dalam struktur yang dipelihara orang lain, dll ...) ke area lain yang disebut tumpukan.

Di tumpukan

Anda tidak perlu tumpukan. Seseorang dapat, secara hipotetis, menulis bahasa yang tidak menggunakan tumpukan (kedalaman arbitrer). BASIC lama yang saya pelajari di masa muda saya melakukannya, seseorang hanya bisa melakukan 8 level gosubpanggilan dan semua variabel bersifat global - tidak ada tumpukan.

Keuntungan dari stack adalah ketika Anda memiliki variabel yang ada dengan cakupan, ketika Anda meninggalkan cakupan itu, frame tumpukan itu muncul. Itu benar-benar menyederhanakan apa yang ada dan apa yang tidak ada. Program pindah ke prosedur lain, bingkai tumpukan baru; program kembali ke prosedur, dan Anda telah kembali ke prosedur yang melihat lingkup Anda saat ini; program meninggalkan prosedur dan semua item pada stack tidak dapat dialokasikan.

Ini benar-benar membuat hidup mudah bagi orang yang menulis runtime untuk kode untuk menggunakan stack dan heap. Mereka hanya banyak konsep dan cara kerja pada kode yang memungkinkan orang yang menulis kode dalam bahasa dibebaskan dari memikirkannya secara eksplisit.

Sifat stack juga berarti tidak bisa terfragmentasi. Fragmentasi memori adalah masalah nyata dengan heap. Anda mengalokasikan beberapa objek, lalu sampah mengumpulkan yang di tengah, dan kemudian mencoba menemukan ruang untuk yang besar berikutnya untuk dialokasikan. Ini berantakan. Mampu meletakkan barang-barang di tumpukan berarti Anda tidak harus berurusan dengan itu.

Ketika sesuatu sampah dikumpulkan

Ketika sesuatu dikumpulkan, sampah itu hilang. Tapi itu hanya sampah yang dikumpulkan karena sudah dilupakan - tidak ada lagi referensi ke objek dalam program yang dapat diakses dari kondisi program saat ini.

Saya akan menunjukkan bahwa ini adalah penyederhanaan pengumpulan sampah yang sangat besar. Ada banyak pengumpul sampah (bahkan di Jawa - Anda dapat men-tweak pengumpul sampah dengan menggunakan berbagai bendera ( docs ). Ini berperilaku berbeda dan nuansa bagaimana masing-masing melakukan hal-hal agak terlalu dalam untuk jawaban ini. Anda mungkin ingin membaca Dasar - Dasar Pengumpulan Sampah Jawa untuk mendapatkan ide yang lebih baik tentang bagaimana beberapa di antaranya berfungsi.

Yang mengatakan, jika sesuatu dialokasikan pada tumpukan, itu bukan sampah yang dikumpulkan sebagai bagian dari System.gc()- itu tidak dialokasikan ketika bingkai tumpukan muncul. Jika ada sesuatu di tumpukan, dan direferensikan dari sesuatu di tumpukan, itu tidak akan menjadi sampah yang dikumpulkan saat itu.

Mengapa ini penting?

Sebagian besar, tradisi. Buku-buku teks yang ditulis dan kelas kompilasi dan berbagai bit dokumentasi membuat masalah besar tentang tumpukan dan tumpukan.

Namun, mesin virtual saat ini (JVM dan sejenisnya) telah berusaha keras untuk mencoba menyembunyikan ini dari programmer. Kecuali jika Anda kehabisan satu dan yang lain dan perlu tahu mengapa (bukan hanya menambah ruang penyimpanan dengan tepat), itu tidak terlalu menjadi masalah.

Objek berada di suatu tempat dan berada di tempat di mana ia dapat diakses dengan benar dan cepat untuk jumlah waktu yang tepat yang ada. Jika ada di tumpukan atau tumpukan - itu tidak masalah.


7
  1. Di heap, sebagai bagian dari objek, yang direferensikan oleh pointer di stack. yaitu. a dan b akan disimpan berdekatan satu sama lain.
  2. Karena jika semua memori adalah tumpukan memori, itu tidak akan efisien lagi. Baik untuk memiliki area kecil dan akses cepat tempat kita memulai dan memiliki item referensi di area memori yang jauh lebih besar. Namun, ini berlebihan ketika sebuah objek hanyalah sebuah primitif tunggal yang akan memakan jumlah ruang yang sama pada stack seperti yang ditunjukkan oleh pointer.
  3. Iya.

1
Saya akan menambahkan ke poin Anda # 2 bahwa jika Anda menyimpan objek di stack (bayangkan objek kamus dengan ratusan ribu entri) maka untuk meneruskannya atau mengembalikannya dari fungsi Anda harus menyalin objek setiap waktu. Dengan menggunakan pointer atau referensi ke objek di heap, kami hanya melewatkan referensi (kecil).
Scott Whitlock

1
Saya pikir 3 akan menjadi 'tidak' karena jika objek sedang dikumpulkan, maka tidak ada referensi di tumpukan yang menunjuk ke sana.
Luciano

@Luciano - Saya mengerti maksud Anda. Saya membaca pertanyaan 3 secara berbeda. "Pada saat yang sama" atau "pada saat itu" adalah implisit. :: shrug ::
pdr

3
  1. Pada heap kecuali Java mengalokasikan instance kelas pada stack sebagai optimisasi setelah membuktikan melalui analisis escape bahwa ini tidak akan mempengaruhi semantik. Ini adalah detail implementasi, jadi, untuk semua tujuan praktis kecuali optimasi mikro jawabannya adalah "on the heap".

  2. Stack memory harus dialokasikan dan dideallocate pada urutan terakhir dengan urutan pertama. Memori tumpukan dapat dialokasikan dan dialokasikan dengan cara apa pun.

  3. Ketika objek sampah dikumpulkan, tidak ada referensi lagi yang menunjuknya dari tumpukan. Jika ada, mereka akan menjaga objek tetap hidup. Primitif tumpukan bukan sampah yang dikumpulkan sama sekali karena mereka secara otomatis dihancurkan ketika fungsi kembali.


3

Memori tumpukan digunakan untuk menyimpan variabel lokal dan panggilan fungsi.

Sedangkan heap memory digunakan untuk menyimpan objek di Jawa. Tidak masalah, di mana objek dibuat dalam kode.

Di mana akan primitif adi class Adisimpan?

Dalam hal ini primitif a dikaitkan dengan objek kelas A. Jadi itu menciptakan Heap Memory.

Mengapa tumpukan memori ada sama sekali? Mengapa kita tidak bisa menyimpan semuanya di stack?

  • Variabel yang dibuat pada stack akan keluar dari ruang lingkup dan secara otomatis dihancurkan.
  • Stack jauh lebih cepat untuk dialokasikan dibandingkan dengan variabel di heap.
  • Variabel di tumpukan harus dihancurkan oleh Pengumpul Sampah.
  • Lebih lambat untuk mengalokasikan dibandingkan dengan variabel pada stack.
  • Anda akan menggunakan stack jika Anda tahu persis berapa banyak data yang perlu Anda alokasikan sebelum waktu kompilasi dan itu tidak terlalu besar. (Variabel lokal primitif disimpan dalam stack)
  • Anda akan menggunakan heap jika Anda tidak tahu persis berapa banyak data yang akan Anda butuhkan saat runtime atau jika Anda perlu mengalokasikan banyak data.

Ketika objek dikumpulkan, apakah tumpukan yang terkait dengan keberatan dihancurkan?

Garbage Collector bekerja di bawah ruang lingkup memori Heap, sehingga menghancurkan objek yang tidak memiliki rantai referensi dari ke root.


Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.