Asli: http://www.mikel.cn/article.asp?id=1698
Tahukah Anda mekanisme pengumpulan sampah .Net? Bisakah Anda menjelaskan secara singkat cara kerja GC? Bagaimana kita bisa mengelola memori secara efektif? Apa peran objek yang dipakai dalam isi pernyataan Menggunakan?
Bagian ini disusun sebagai berikut, 1. Jenis jaringan dan alokasi memori 2. Cara kerja pengumpul sampah GC 3. Apa yang dimaksud dengan sumber daya yang tidak dikelola 4. Cara melepaskan sumber daya objek secara efektif. Ringkasan. Mari kita mulai mempelajari bagian ini sekarang.
1..Jenis bersih dan alokasi memori
Semua tipe di Net diturunkan (secara langsung atau tidak langsung) dari tipe System.Object.
Tipe di CTS dibagi menjadi dua kategori - tipe referensi (tipe referensi, juga disebut tipe terkelola), yang dialokasikan di heap memori, dan tipe nilai. Tipe nilai dialokasikan pada tumpukan. Seperti yang ditunjukkan pada gambar
Tipe nilai ada di tumpukan, pertama masuk, terakhir keluar. Variabel tipe nilai memiliki urutan masa pakai. Hal ini memastikan bahwa variabel tipe nilai akan melepaskan sumber daya sebelum dikeluarkan dari cakupannya. Lebih sederhana dan lebih efisien daripada tipe referensi. Tumpukan mengalokasikan memori dari alamat tinggi ke alamat rendah.
Tipe referensi dialokasikan pada heap terkelola (Managed Heap), dan sebuah variabel dideklarasikan dan disimpan pada stack. Ketika new digunakan untuk membuat sebuah objek, alamat objek tersebut disimpan dalam variabel ini. Sebaliknya, tumpukan terkelola mengalokasikan memori dari alamat rendah ke alamat tinggi, seperti yang ditunjukkan pada gambar
2. Cara kerja pengumpul sampah GC
Pada gambar di atas, ketika dataSet kedaluwarsa, kami tidak menampilkan pemusnahan objek, dan objek di heap tetap ada, menunggu daur ulang GC.
Direkomendasikan namun tidak diwajibkan agar pengumpul sampah mendukung penuaan objek dari generasi ke generasi. Generasi adalah unit objek dengan usia relatif dalam memori. Obyek
Nama kode atau usia mengidentifikasi generasi milik objek tersebut. Dalam siklus hidup aplikasi, objek yang dibuat lebih baru adalah milik generasi yang lebih baru dan memiliki nilai lebih tinggi dibandingkan objek yang dibuat sebelumnya.
Nomor sub-kode yang lebih rendah. Kode objek pada generasi terbaru adalah 0.
Saat membuat objek baru, Anda harus terlebih dahulu mencari daftar tertaut gratis untuk menemukan blok memori yang paling sesuai, mengalokasikannya, menyesuaikan daftar tertaut blok memori, dan menggabungkan fragmen. Operasi baru dapat diselesaikan dalam waktu hampir O(1), dengan menambahkan 1 ke penunjuk teratas heap. Prinsip kerjanya adalah: Ketika ruang yang tersisa di heap terkelola tidak mencukupi, atau ruang Generator 0 penuh, GC berjalan dan mulai mengambil kembali memori. Pada awal pengumpulan sampah, GC mengompresi dan menyesuaikan memori heap, dan objek terkonsentrasi di bagian atas. GC akan menghabiskan sejumlah waktu CPU saat memindai sampah. Algoritme GC asli benar-benar memindai seluruh heap, sehingga tidak efisien. GC saat ini membagi objek di heap menjadi tiga generasi. Entri terbaru ke heap adalah generasi 0, diikuti oleh generasi 1 dan generasi 2. GC pertama hanya memindai generasi 0. Jika ruang yang diperoleh kembali cukup untuk penggunaan saat ini, maka tidak perlu memindai objek di generasi lain. Oleh karena itu, GC membuat objek lebih efisien daripada C++ dan tidak perlu memindai seluruh ruang heap. Peningkatan kinerja yang dihasilkan oleh strategi pemindaian dan strategi manajemen memori cukup untuk mengimbangi waktu CPU yang digunakan oleh GC.
3. Apa yang dimaksud dengan sumber daya yang tidak dikelola?
Sumber daya umum yang tidak dikelola adalah objek yang menggabungkan sumber daya sistem operasi, seperti file, jendela, atau koneksi jaringan. Untuk sumber daya tersebut, meskipun pengumpul sampah dapat melacak masa pakai objek yang menggabungkan sumber daya yang tidak dikelola, ia mengetahui caranya membersihkan sumber daya ini. Untungnya, metode Finalize() yang disediakan oleh .net Framework memungkinkan sumber daya yang tidak dikelola dibersihkan dengan benar sebelum pengumpul sampah mendaur ulang sumber daya tersebut. Berikut adalah beberapa sumber daya umum yang tidak dikelola: kuas, objek aliran, objek komponen, dan sumber daya lainnya (Object, OdbcDataReader, OleDBDataReader, Pen, Regex, Socket, StreamWriter, ApplicationContext, Brush,
Komponen, Perancang Komponen, Wadah, Konteks, Kursor, FileStream,
Font, Ikon, Gambar, Matriks, Timer, Tooltip). (Lihat MSDN)
4. Cara melepaskan sumber daya yang tidak dikelola secara efektif.
GC tidak dapat mengelola sumber daya yang tidak dikelola, jadi bagaimana cara melepaskan sumber daya yang tidak dikelola? .Net menyediakan dua metode:
(1) Destruktor: Ketika pengumpul sampah mendaur ulang sumber daya dari objek yang tidak dikelola, ia akan memanggil metode finalisasi objek Finalize() untuk membersihkan sumber daya. Namun, karena keterbatasan aturan kerja GC, GC memanggil Finalisasi objek metode. Sumber daya tidak akan dirilis satu kali, dan objek akan dihapus setelah panggilan kedua.
(2) Mewarisi antarmuka IDisposable dan menerapkan metode Dispose(). Antarmuka IDisposable mendefinisikan pola (dengan dukungan tingkat bahasa), menyediakan mekanisme tertentu untuk melepaskan sumber daya yang tidak dikelola, dan menghindari masalah yang melekat pada perangkat pengumpulan sampah -masalah terkait.
Untuk lebih memahami mekanisme pengumpulan sampah, saya secara khusus menulis sebagian kode dan menambahkan komentar mendetail. Tentukan satu kelas FrankClassWithDispose (mewarisi antarmuka IDisposable), FrankClassNoFinalize (tanpa finalizer), FrankClassWithDestructor (mendefinisikan destruktor).
Kode spesifiknya adalah sebagai berikut:
Kode
1 menggunakan Sistem;
2 menggunakan System.Collections.Generic;
3 menggunakan Sistem.Teks;
4 menggunakan Sistem.Data;
5 menggunakan Sistem.Data.Odbc;
6 menggunakan System.Drawing;
7 // Dikodekan Oleh Frank Xu Lei 18/2/2009
8 // Pelajari Manajemen Memori .NET
9 // Pengumpul Sampah Pengumpul Sampah. Sumber daya yang dihosting dapat diambil kembali bila diperlukan sesuai dengan kebijakan,
10 // Namun GC tidak tahu cara mengelola sumber daya yang tidak dikelola. Seperti koneksi jaringan, koneksi database, kuas, komponen, dll.
11 //Dua mekanisme untuk memecahkan masalah pelepasan sumber daya yang tidak dikelola. Destruktor, antarmuka IDispose
12 // Jumlah referensi COM
13 // Manajemen manual C++, Hapus Baru
14 // Manajemen otomatis VB
15 Manajemen Memori namespace
16 {
17 // Mewarisi antarmuka IDisposable, mengimplementasikan metode Dispose, dan melepaskan sumber daya instance FrankClassDispose
18 kelas publik FrankClassWithDispose : IDisposable
19 {
20 OdbcConnection pribadi _odbcConnection = null;
dua puluh satu
22 // Konstruktor
23 FrankClassWithDispose publik()
dua puluh empat {
25 jika (_odbcConnection == null )
26 _odbcConnection = OdbcConnection baru();
27 Console.WriteLine( " FrankClassWithDispose telah dibuat " );
28 }
29 //Metode pengujian
30 kekosongan publik Lakukan Sesuatu()
31 {
32
33 /**/ /// /code di sini untuk melakukan sesuatu
34 kembali;
35}
36 // Implementasi Buang dan lepaskan sumber daya yang digunakan oleh kelas ini
37 kekosongan publik Buang()
38 {
39 jika (_odbcConnection != null )
40 _odbcConnection.Buang();
41 Console.WriteLine( " FrankClassWithDispose telah dibuang " );
42 }
43}
44 // Finalisasi tidak diterapkan, tunggu hingga GC mendaur ulang sumber daya instance FrankClassFinalize, dan mendaur ulangnya secara langsung saat GC sedang berjalan.
45 kelas publik FrankClassNoFinalize
46 {
47 OdbcConnection pribadi _odbcConnection = null ;
48 //Konstruktor
49 publik FrankClassNoFinalize()
50 {
51 jika (_odbcConnection == null )
52 _odbcConnection = OdbcConnection baru();
53 Console.WriteLine( " FrankClassNoFinalize telah dibuat " );
54 }
55 //Metode pengujian
56 kekosongan publik Lakukan Sesuatu()
57 {
58
59 // GC.Kumpulkan();
60 /**/ /// /code di sini untuk melakukan sesuatu
61 kembali;
62 }
63}
64 // Implementasikan destruktor, kompilasi ke dalam metode Finalisasi, dan panggil destruktor objek
65 // Saat GC berjalan, sumber daya tidak dilepaskan pada panggilan pertama tetapi hanya pada panggilan kedua.
66 //Sumber daya instance FrankClassDestructor
67 // CLR menggunakan thread independen untuk mengeksekusi metode Finalisasi objek. Panggilan yang sering akan menurunkan kinerja.
68 kelas publik FrankClassWithDestructor
69 {
70 OdbcConnection pribadi _odbcConnection = null ;
71 //Konstruktor
72 FrankClassWithDestructor publik()
73 {
74 jika (_odbcConnection == null )
75 _odbcConnection = OdbcConnection baru();
76 Console.WriteLine( " FrankClassWithDestructor telah dibuat " );
77 }
78 //Metode pengujian
79 kekosongan publik Lakukan Sesuatu()
80 {
81 /**/ /// /code di sini untuk melakukan sesuatu
82
83 kembali;
84}
85 // Destructor, melepaskan sumber daya yang tidak dikelola
86 ~ FrankClassWithDestructor()
87 {
88 jika (_odbcConnection != null )
89 _odbcConnection.Buang();
90 Console.WriteLine( " FrankClassWithDestructor telah dibuang " );
91 }
92 }
93}
94
Contoh objek OdbcConnection yang tidak dikelola digunakan. Klien yang dibangun diuji sebentar. Kode kliennya adalah sebagai berikut:
Kode
1 menggunakan Sistem;
2 menggunakan System.Collections.Generic;
3 menggunakan Sistem.Teks;
4 menggunakan Sistem.Data;
5 menggunakan Manajemen Memori;
6 // Dikodekan Oleh Frank Xu Lei 18/2/2009
7 // Pelajari Manajemen Memori .NET
8 // Menguji Objek Tak Terkelola yang Direklamasi.
9 // Pengujian untuk kode yang tidak dikelola, perbandingan
10 // Untuk kode yang dikelola, GC dapat mendaur ulangnya sendiri secara lebih strategis, atau dapat mengimplementasikan IDisposable, memanggil metode Dispose(), dan melepaskannya secara aktif.
11 namespace MemoryManagementClient
12 {
13 program kelas
14 {
15 kekosongan statis Utama (string [] args)
16 {
17
18 /**/ ////////////////////////////////////////// //(1 ) / ///////////////////////////////////////////// //
19 //Panggil metode Dispose() untuk melepaskan secara aktif. sumber daya, fleksibilitas
20 FrankClassWithDispose _frankClassWithDispose = null ;
21 mencoba
dua puluh dua {
23 _frankClassWithDispose = FrankClassWithDispose baru();
24 _frankClassWithDispose.DoSomething();
25
26}
27 akhirnya
28 {
29 jika (_frankClassWithDispose != null )
30 _frankClassWithDispose.Dispose();
31 // Console.WriteLine("Instans FrankClassWithDispose telah dirilis");
32}
33
34 /**/ ////////////////////////////////////////// //(2 ) / ///////////////////////////////////////////////// /
35 // Anda dapat menggunakan pernyataan Menggunakan untuk membuat objek yang tidak dikelola sebelum eksekusi metode berakhir, objek tersebut akan dipanggil
36 menggunakan (FrankClassWithDispose _frankClassWithDispose2 = new FrankClassWithDispose())
37 {
38 // _frankClassWithDispose2.DoSomething();
39 }
40
41 /**/ ////////////////////////////////////////// //(3 ) / ///////////////////////////////////////////// //
42 //Saat pengumpul sampah berjalan, sumber daya dilepaskan satu kali
43 FrankClassNoFinalize _frankClassNoFinalize = FrankClassNoFinalize();
44 _frankClassNoFinalize.DoSomething();
45
46 /**/ /////////////////////////////////////////////// (4) ///////////////////////////////////////////////// / /
47 // Saat pengumpul sampah sedang berjalan, dibutuhkan dua kali untuk melepaskan sumber daya.
48 FrankClassWithDestructor _frankClassWithDestructor = FrankClassWithDestructor();
49 _frankClassWithDestructor.DoSomething();
50 /**/ ////////////////////////////////////////////// / (5 ) ///////////////////////////////////////////////// /
51 // Pernyataan Menggunakan tidak dapat digunakan untuk membuat objek karena tidak mengimplementasikan antarmuka IDispose.
52 // menggunakan (FrankClassWithDestructor _frankClassWithDestructor2 = FrankClassWithDestructor() baru)
53 // {
54 // _frankClassWithDestructor2.DoSomething();
55 // }
56
57 /**/ /////////////////////////////////////////////// /// /////////////////////////////////////////// //
58 // Untuk Debug
59 Console.WriteLine( "Tekan sembarang tombol untuk melanjutkan" );
60 Konsol.ReadLine();
61
62
63}
64}
65 }
66
Terkadang sumber daya harus dilepaskan pada waktu tertentu. Sebuah kelas dapat mengimplementasikan antarmuka IDisposable yang melakukan pengelolaan sumber daya dan metode tugas pembersihan IDisposable.Dispose.
Jika pemanggil perlu memanggil metode Dispose untuk membersihkan objek, kelas harus mengimplementasikan metode Dispose sebagai bagian dari kontrak. Pengumpul sampah tidak menelepon secara default
Metode Buang; namun, penerapan metode Buang dapat memanggil metode di GC untuk mengatur perilaku akhir pengumpul sampah.
Perlu disebutkan bahwa: memanggil metode Dispose() secara aktif melepaskan sumber daya dan fleksibel. Anda dapat menggunakan pernyataan Menggunakan untuk membuat objek yang tidak dikelola Sebelum eksekusi metode berakhir, metode tersebut akan dipanggil
Metode Dispose() melepaskan sumber daya. Efek dari kedua ujung kode adalah sama.
Kode
1. mencoba
2 {
3 IL_0003: tidak
4 IL_0004: instance objek baru batal [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
5 IL_0009: stlok 0
6 IL_000a: ldloc
7 IL_000b: instance callvirt batal [MemoryManagement]MemoryManagement.FrankClassWithDispose::DoSomething()
8 IL_0010: tidak
9 IL_0011: tidak
10 IL_0012: berangkat IL_0028
11 } // akhiri .coba
12 akhirnya
13 {
14 IL_0014: tidak
15 IL_0015: lokasi 0
16 IL_0016: ldnull
17 IL_0017: ceq
18 IL_0019: stloc.s CS$4$0000
19 IL_001b: ldloc.s CS$4$0000
20 IL_001d: benar.s IL_0026
21 IL_001f: ldloc
22 IL_0020: instance callvirt batal [MemoryManagement]MemoryManagement.FrankClassWithDispose::Dispose()
23 IL_0025: tidak
24 IL_0026: tidak
25 IL_0027: akhirnya
26 } // pengendali akhir
27 IL_0028: tidak
28 IL_0029: instance objek baru batal [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
29 IL_002e: stlok 1
30. mencoba
31 {
32 IL_002f: tidak
33 IL_0030: tidak
34 IL_0031: berangkat IL_0045
35 } // akhiri .coba
36 akhirnya
37 {
38 IL_0033: lokasi 1
39 IL_0034: batal
40 IL_0035: ceq
41 IL_0037: stloc.s CS$4$0000
42 IL_0039: ldloc.s CS$4$0000
43 IL_003b: salah.s IL_0044
44 IL_003d: lokasi 1
45 IL_003e: instance callvirt batal [mscorlib]System.IDisposable::Dispose()
46 IL_0043: tidak
47 IL_0044: akhirnya
48 } // pengendali akhir
49
Pernyataan Menggunakan memiliki efek yang sama untuk melepaskan sumber daya objek yang tidak dikelola. Hal ini sering ditemui dalam wawancara, seperti apa kegunaan kata kunci Menggunakan dan pertanyaan sejenisnya. Jawaban dasar yang ideal adalah selain mereferensikan namespace dan menyetel alias untuk namespace tersebut, penggunaan ini mewujudkan daur ulang sumber daya objek yang tidak dikelola seperti blok try akhirnya. Hanya cara sederhana untuk menulisnya.
Saat Anda menggunakan metode Buang untuk melepaskan objek yang tidak dikelola, Anda harus memanggil GC.SuppressFinalize. Jika objek berada dalam antrian finalisasi, GC.SuppressFinalize akan mencegah GC memanggil metode Finalize. Karena memanggil metode Finalize akan mengorbankan beberapa performa. Jika metode Dispose Anda telah membersihkan sumber daya yang didelegasikan, GC tidak perlu memanggil metode Finalisasi objek (MSDN) lagi. Terlampir adalah kode MSDN untuk referensi Anda.
Kode
BaseResource kelas publik: IDisposable
{
//Menunjuk ke sumber daya eksternal yang tidak dikelola
pegangan IntPtr pribadi;
// Sumber daya terkelola lainnya yang digunakan oleh kelas ini.
Komponen Komponen pribadi;
// Lacak apakah metode .Dispose dipanggil, tandai bit, kendalikan perilaku pengumpul sampah
private bool dibuang = false ;
//Konstruktor
Sumber Daya Basis publik()
{
// Masukkan kode konstruktor yang sesuai di sini.
}
// Implementasikan antarmuka IDisposable.
// Tidak dapat dideklarasikan sebagai metode virtual virtual.
// Subkelas tidak dapat mengesampingkan metode ini.
kekosongan publik Buang()
{
Buang( benar );
//Tinggalkan antrian Finalisasi
//Mengatur kode finalizer pemblokiran objek
//
GC.SuppressFinalize( ini );
}
// Buang(bool membuang) dijalankan dalam dua situasi berbeda.
// Jika membuang sama dengan benar, metode telah dipanggil
// Atau dipanggil secara tidak langsung dengan kode pengguna. Baik kode terkelola maupun tidak terkelola dapat dilepaskan
// Jika membuang sama dengan false, metode telah dipanggil secara internal oleh finalizer,
// Anda tidak dapat mereferensikan objek lain, hanya sumber daya yang tidak dikelola yang dapat dilepaskan.
kekosongan virtual yang dilindungi Buang (pembuangan bool)
{
// Periksa apakah Dispose telah dipanggil.
jika ( ! ini .dibuang)
{
// Jika sama dengan benar, lepaskan semua sumber daya yang dikelola dan tidak dikelola
jika (membuang)
{
// Rilis sumber daya yang dikelola.
Komponen.Buang();
}
// Melepaskan sumber daya yang tidak dikelola, jika pembuangannya salah,
// Hanya kode berikut yang akan dieksekusi.
CloseHandle(pegangan);
menangani = IntPtr.Zero;
// Perhatikan bahwa ini tidak aman untuk thread.
// Setelah sumber daya terkelola dilepaskan, thread lain dapat dimulai untuk menghancurkan objek.
// Tapi sebelum flag yang dibuang disetel ke true
// Jika keamanan thread diperlukan, klien harus mengimplementasikannya.
}
dibuang = benar;
}
//Gunakan interop untuk memanggil metode
// Hapus sumber daya yang tidak dikelola.
[Sistem.Runtime.InteropServices.DllImport( " Kernel32 " )]
Boolean CloseHandle statis eksternal pribadi (pegangan IntPtr);
//Gunakan destruktor C# untuk mengimplementasikan kode finalizer
// Ini hanya bisa dipanggil dan dieksekusi jika metode Dispose belum dipanggil.
// Jika Anda memberi kesempatan pada kelas dasar untuk menyelesaikannya.
// Jangan sediakan destruktor untuk subkelas.
~ Sumber Daya Basis ()
{
// Jangan membuat ulang kode pembersihan.
// Berdasarkan pertimbangan keandalan dan pemeliharaan, memanggil Dispose(false) adalah cara terbaik
Buang( salah );
}
// Memungkinkan Anda memanggil metode Buang beberapa kali,
// Namun pengecualian akan dilempar jika objek telah dilepaskan.
// Kapan pun Anda memproses objek, Anda akan memeriksa apakah objek tersebut dilepaskan.
// periksa apakah sudah dibuang.
kekosongan publik Lakukan Sesuatu()
{
jika (ini .dibuang)
{
melempar ObjectDisposeException();
}
}
Untuk tipe yang pemanggilan metode Close lebih alami dibandingkan metode Dispose, Anda dapat menambahkan metode Close ke kelas dasar.
Metode Tutup tidak mengambil parameter dan memanggil metode Buang yang melakukan pekerjaan pembersihan yang sesuai.
Contoh berikut menunjukkan metode Close.
// Jangan setel metode ke virtual.
// Kelas yang diwarisi tidak diperbolehkan untuk mengganti metode ini
kekosongan publik Tutup()
{
// Panggil parameter Buang tanpa parameter.
Membuang();
}
kekosongan statis publik Utama()
{
//Masukkan kode di sini untuk membuat
// dan gunakan objek BaseResource.
}