Array Anda dialokasikan pada heap, dan int tidak kotak.
Sumber kebingungan Anda kemungkinan karena orang mengatakan bahwa tipe referensi dialokasikan pada heap, dan tipe nilai dialokasikan pada tumpukan. Ini bukan representasi yang sepenuhnya akurat.
Semua variabel dan parameter lokal dialokasikan pada tumpukan. Ini termasuk tipe nilai dan tipe referensi. Perbedaan antara keduanya hanyalah apa yang disimpan dalam variabel. Tidak mengherankan, untuk tipe nilai, nilai tipe disimpan langsung dalam variabel, dan untuk tipe referensi, nilai tipe disimpan di heap, dan referensi ke nilai ini adalah apa yang disimpan dalam variabel.
Hal yang sama berlaku untuk bidang. Ketika memori dialokasikan untuk instance dari tipe agregat (a class
atau astruct
), itu harus menyertakan penyimpanan untuk masing-masing bidang instance. Untuk bidang tipe referensi, penyimpanan ini hanya menyimpan referensi ke nilai, yang akan dialokasikan pada tumpukan nanti. Untuk bidang tipe nilai, penyimpanan ini menyimpan nilai aktual.
Jadi, diberikan jenis berikut:
class RefType{
public int I;
public string S;
public long L;
}
struct ValType{
public int I;
public string S;
public long L;
}
Nilai dari masing-masing jenis ini akan membutuhkan 16 byte memori (dengan asumsi ukuran kata 32-bit). Bidang I
dalam setiap kasus membutuhkan 4 byte untuk menyimpan nilainya, bidang S
membutuhkan 4 byte untuk menyimpan referensi, dan bidang L
membutuhkan 8 byte untuk menyimpan nilainya. Jadi memori untuk nilai keduanya RefType
dan ValType
terlihat seperti ini:
0 ┌──────────────────┐
│ I │
4 ├──────────────────┤
│ S │
8 ├──────────────────┤
│ L │
│ │
16 └──────────────────┘
Sekarang jika Anda memiliki tiga variabel lokal dalam suatu fungsi, jenis RefType
, ValType
dan int[]
, seperti ini:
RefType refType;
ValType valType;
int[] intArray;
maka tumpukan Anda mungkin terlihat seperti ini:
0 ┌──────────────────┐
│ refType │
4 ├──────────────────┤
│ valType │
│ │
│ │
│ │
20 ├──────────────────┤
│ intArray │
24 └──────────────────┘
Jika Anda menetapkan nilai ke variabel lokal ini, seperti:
refType = new RefType();
refType.I = 100;
refType.S = "refType.S";
refType.L = 0x0123456789ABCDEF;
valType = new ValType();
valType.I = 200;
valType.S = "valType.S";
valType.L = 0x0011223344556677;
intArray = new int[4];
intArray[0] = 300;
intArray[1] = 301;
intArray[2] = 302;
intArray[3] = 303;
Maka tumpukan Anda mungkin terlihat seperti ini:
0 ┌──────────────────┐
│ 0x4A963B68 │ - heap address `refType`
4 ├──────────────────┤
│ 200 │ - nilai `valType.I`
│ 0x4A984C10 │ - tumpukan alamat `valType.S`
│ 0x44556677 │ - `valType.L` 32-bit yang rendah
│ 0x00112233 │ - `valType.L` 32-bit yang tinggi
20 ├──────────────────┤
│ 0x4AA4C288 │ - heap address `intArray`
24 └──────────────────┘
Memori di alamat 0x4A963B68
(nilai refType
) akan menjadi seperti:
0 ┌──────────────────┐
│ 100 │ - nilai `refType.I`
4 ├──────────────────┤
│ 0x4A984D88 │ - heap address `refType.S`
8 ├──────────────────┤
│ 0x89ABCDEF │ - `refType.L` 32-bit yang rendah
│ 0x01234567 │ - `refType.L` 32-bit yang tinggi
16 └──────────────────┘
Memori di alamat 0x4AA4C288
(nilai intArray
) akan menjadi seperti:
0 ┌──────────────────┐
│ 4 │ - panjang array
4 ├──────────────────┤
│ 300 │ - `intArray [0]`
8 ├──────────────────┤
│ 301 │ - `intArray [1]`
12 ├──────────────────┤
│ 302 │ - `intArray [2]`
16 ├──────────────────┤
│ 303 │ - `intArray [3]`
20 └──────────────────┘
Sekarang, jika Anda beralih intArray
ke fungsi lain, nilai yang didorong ke stack akan menjadi 0x4AA4C288
, alamat array, bukan salinan array.
Ya array akan ditemukan di heap.
Int di dalam array tidak akan dikotak. Hanya karena tipe nilai ada di heap, tidak berarti itu akan kotak. Boxing hanya akan terjadi ketika tipe nilai, seperti int, ditugaskan untuk referensi objek tipe.
Sebagai contoh
Tidak kotak:
Kotak:
Anda mungkin juga ingin melihat posting Eric tentang hal ini:
sumber
Untuk memahami apa yang terjadi, berikut adalah beberapa fakta:
Jadi, jika Anda memiliki array bilangan bulat, array dialokasikan pada heap dan bilangan bulat yang dikandungnya adalah bagian dari objek array di heap. Bilangan bulat berada di dalam objek array di heap, bukan sebagai objek terpisah, sehingga tidak kotak.
Jika Anda memiliki array string, itu benar-benar array referensi string. Karena referensi adalah tipe nilai, mereka akan menjadi bagian dari objek array di heap. Jika Anda meletakkan objek string dalam array, Anda sebenarnya meletakkan referensi ke objek string dalam array, dan string adalah objek terpisah di heap.
sumber
Saya pikir inti dari pertanyaan Anda terletak pada kesalahpahaman tentang jenis referensi dan nilai. Ini adalah sesuatu yang mungkin diperjuangkan oleh setiap pengembang .NET dan Java.
Array hanyalah daftar nilai. Jika itu adalah array dari tipe referensi (katakanlah a
string[]
) maka array adalah daftar referensi ke berbagaistring
objek di heap, karena referensi adalah nilai dari tipe referensi. Secara internal, referensi ini diimplementasikan sebagai pointer ke alamat dalam memori. Jika Anda ingin memvisualisasikan ini, array seperti itu akan terlihat seperti ini di memori (di heap):[ 00000000, 00000000, 00000000, F8AB56AA ]
Ini adalah array
string
yang berisi 4 referensi kestring
objek di heap (angka-angka di sini adalah heksadesimal). Saat ini, hanya yang terakhir yangstring
benar-benar menunjuk ke sesuatu (memori diinisialisasi ke nol ketika dialokasikan), array ini pada dasarnya akan menjadi hasil dari kode ini di C #:Array di atas akan dalam program 32 bit. Dalam program 64 bit, referensi akan dua kali lebih besar (
F8AB56AA
akan00000000F8AB56AA
).Jika Anda memiliki sebuah array jenis nilai (mengatakan sebuah
int[]
) maka array adalah daftar bilangan bulat, sebagai nilai dari jenis nilai adalah nilai itu sendiri (maka nama). Visualisasi array seperti ini adalah sebagai berikut:[ 00000000, 45FF32BB, 00000000, 00000000 ]
Ini adalah array dari 4 bilangan bulat, di mana hanya int kedua diberikan nilai (ke 1174352571, yang merupakan representasi desimal dari angka heksadesimal) dan sisa bilangan bulat akan menjadi 0 (seperti yang saya katakan, memori diinisialisasi ke nol dan 00000000 dalam heksadesimal adalah 0 dalam desimal). Kode yang menghasilkan array ini adalah:
int[]
Array ini juga akan disimpan di heap.Sebagai contoh lain, memori
short[4]
array akan terlihat seperti ini:[ 0000, 0000, 0000, 0000 ]
Karena nilai a
short
adalah angka 2 byte.Di mana tipe nilai disimpan, hanyalah detail implementasi seperti yang dijelaskan Eric Lippert dengan sangat baik di sini , tidak melekat pada perbedaan antara nilai dan tipe referensi (yang merupakan perbedaan perilaku).
Ketika Anda melewati sesuatu untuk metode (bahwa tipe referensi atau tipe nilai) kemudian copy dari nilai dari jenis yang benar-benar melewati ke metode. Dalam kasus tipe referensi, nilainya adalah referensi (anggap ini sebagai penunjuk ke sepotong memori, meskipun itu juga merupakan detail implementasi) dan dalam kasus tipe nilai, nilainya adalah benda itu sendiri.
Boxing hanya terjadi jika Anda mengonversi tipe nilai ke tipe referensi. Kotak kode ini:
sumber
Ini adalah ilustrasi yang menggambarkan jawaban di atas oleh @P Daddy
Dan saya menggambarkan konten yang sesuai dengan gaya saya.
sumber
Array bilangan bulat dialokasikan pada heap, tidak lebih, tidak kurang. referensi myIntegers ke awal bagian di mana int dialokasikan. Referensi itu terletak di tumpukan.
Jika Anda memiliki array objek tipe referensi, seperti tipe Object, myObjects [], yang terletak di stack, akan merujuk ke sekelompok nilai yang mereferensikan objek itu sendiri.
Untuk meringkas, jika Anda meneruskan myIntegers ke beberapa fungsi, Anda hanya meneruskan referensi ke tempat di mana kumpulan nyata bilangan bulat dialokasikan.
sumber
Tidak ada tinju dalam kode contoh Anda.
Jenis nilai dapat hidup di heap seperti yang mereka lakukan di array int Anda. Array dialokasikan pada heap dan menyimpan int, yang kebetulan merupakan tipe nilai. Isi array diinisialisasi ke default (int), yang kebetulan nol.
Pertimbangkan kelas yang berisi tipe nilai:
Variabel h merujuk pada instance HasAnInt yang hidup di heap. Kebetulan mengandung tipe nilai. Tidak apa-apa, 'aku' kebetulan tinggal di tumpukan karena itu terkandung dalam kelas. Tidak ada tinju dalam contoh ini juga.
sumber
Cukup sudah dikatakan oleh semua orang, tetapi jika seseorang mencari sampel dan dokumentasi yang jelas (tetapi tidak resmi) tentang heap, stack, variabel lokal, dan variabel statis, lihat artikel lengkap Jon Skeet tentang Memori di .NET - apa yang terjadi dimana
Kutipan:
Setiap variabel lokal (yaitu satu yang dideklarasikan dalam suatu metode) disimpan di stack. Itu termasuk variabel tipe referensi - variabel itu sendiri ada di stack, tetapi ingat bahwa nilai variabel tipe referensi hanya referensi (atau nol), bukan objek itu sendiri. Parameter metode juga dihitung sebagai variabel lokal, tetapi jika mereka dideklarasikan dengan pengubah ref, mereka tidak mendapatkan slotnya sendiri, tetapi berbagi slot dengan variabel yang digunakan dalam kode panggilan. Lihat artikel saya tentang parameter yang lewat untuk detail lebih lanjut.
Variabel instan untuk tipe referensi selalu ada di heap. Di situlah objek itu sendiri "hidup".
Variabel instan untuk tipe nilai disimpan dalam konteks yang sama dengan variabel yang mendeklarasikan tipe nilai. Slot memori untuk instance secara efektif berisi slot untuk setiap bidang dalam instance. Itu berarti (mengingat dua poin sebelumnya) bahwa variabel struct dideklarasikan dalam suatu metode akan selalu berada di stack, sedangkan variabel struct yang merupakan bidang contoh kelas akan berada di heap.
Setiap variabel statis disimpan di heap, terlepas dari apakah itu dinyatakan dalam tipe referensi atau tipe nilai. Hanya ada satu slot total tidak peduli berapa banyak instance yang dibuat. (Tidak perlu ada instance yang dibuat untuk slot yang ada sekalipun.) Rincian yang tepat dari tumpukan variabel hidup rumit, tetapi dijelaskan secara rinci dalam artikel MSDN tentang subjek.
sumber