Pertanyaan Penggunaan yang tepat dari antarmuka IDisposable


Saya tahu dari membaca dokumentasi MSDN bahwa penggunaan "primer" dari IDisposable antarmuka adalah untuk membersihkan sumber daya yang tidak dikelola.

Bagi saya, "unmanaged" berarti hal-hal seperti koneksi database, soket, handle jendela, dll. Tapi, saya pernah melihat kode di mana Dispose() metode diimplementasikan secara gratis berhasil sumber daya, yang tampaknya berlebihan bagi saya, karena pengumpul sampah harus mengurusnya untuk Anda.

Sebagai contoh:

public class MyCollection : IDisposable
{
    private List<String> _theList = new List<String>();
    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();

    // Die, clear it up! (free unmanaged resources)
    public void Dispose()
    {
        _theList.clear();
        _theDict.clear();
        _theList = null;
        _theDict = null;
    }

Pertanyaan saya adalah, apakah ini membuat memori bebas pengumpul sampah digunakan oleh MyCollection lebih cepat dari biasanya?

sunting: Sejauh ini orang telah memposting beberapa contoh bagus menggunakan IDisposable untuk membersihkan sumber daya yang tidak dikelola seperti koneksi database dan bitmap. Tetapi anggaplah itu _theList dalam kode di atas berisi sejuta string, dan Anda ingin membebaskan ingatan itu sekarang, daripada menunggu pengumpul sampah. Apakah kode di atas akan mencapai itu?


1378
2018-02-11 18:12


asal


Jawaban:


Titik Buang aku s untuk membebaskan sumber daya yang tidak dikelola. Itu perlu dilakukan di beberapa titik, kalau tidak mereka tidak akan pernah dibersihkan. Pengumpul sampah tidak tahu bagaimana memanggil DeleteHandle() pada variabel tipe IntPtr, itu tidak tahu apakah atau tidak perlu dihubungi DeleteHandle().

Catatan: Apa itu sumber daya yang tidak dikelola? Jika Anda menemukannya di Microsoft .NET Framework: itu dikelola. Jika Anda mengaduk-aduk MSDN sendiri, itu tidak dikelola. Apa pun yang Anda gunakan P / Memanggil panggilan untuk keluar dari dunia yang nyaman dan nyaman dari segala yang tersedia untuk Anda. Framwork NET tidak dikelola - dan Anda sekarang bertanggung jawab untuk membersihkannya.

Objek yang Anda buat harus diekspos beberapa metode, yang dapat dipanggil oleh dunia luar, untuk membersihkan sumber daya yang tidak dikelola. Metode ini dapat diberi nama apa pun yang Anda suka:

public void Cleanup()

public void Shutdown()

Tetapi sebaliknya ada nama standar untuk metode ini:

public void Dispose()

Bahkan ada antarmuka yang dibuat, IDisposable, yang hanya memiliki satu metode itu:

public interface IDisposable
{
   void Dispose()
}

Jadi Anda membuat objek Anda mengekspos IDisposable antarmuka, dan dengan cara itu Anda berjanji bahwa Anda telah menulis bahwa metode tunggal untuk membersihkan sumber daya yang tidak dikelola:

public void Dispose()
{
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}

Dan selesai. Kecuali Anda bisa berbuat lebih baik.


Bagaimana jika objek Anda telah mengalokasikan 250MB System.Drawing.Bitmap (yaitu. NET dikelola kelas Bitmap) sebagai semacam frame buffer? Tentu, ini adalah objek .NET yang dikelola, dan garbage collector akan membebaskannya. Tapi apakah Anda benar-benar ingin meninggalkan memori 250MB hanya duduk di sana - menunggu tukang sampah akhirnya datang dan membebaskannya? Bagaimana jika ada koneksi database terbuka? Tentunya kita tidak ingin koneksi itu terbuka, menunggu GC untuk menyelesaikan objek.

Jika pengguna telah menelepon Dispose() (berarti mereka tidak lagi berencana untuk menggunakan objek) mengapa tidak menyingkirkan bitmap dan koneksi database yang sia-sia itu?

Jadi sekarang kita akan:

  • singkirkan sumber daya yang tidak dikelola (karena kita harus), dan
  • menyingkirkan sumber daya yang dikelola (karena kami ingin membantu)

Jadi mari perbarui kami Dispose() metode untuk menyingkirkan objek-objek yang dikelola:

public void Dispose()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //Free managed resources too
   if (this.databaseConnection != null)
   {
      this.databaseConnection.Dispose();
      this.databaseConnection = null;
   }
   if (this.frameBufferImage != null)
   {
      this.frameBufferImage.Dispose();
      this.frameBufferImage = null;
   }
}

Dan semuanya bagus, kecuali Anda bisa lebih baik!


Bagaimana kalau orang itu lupa memanggil Dispose() pada objekmu? Kemudian mereka akan membocorkan beberapa tidak dikelola sumber daya!

catatan: Mereka tidak akan bocor berhasil sumber daya, karena pada akhirnya pengumpul sampah akan berjalan, pada utas latar belakang, dan membebaskan memori yang terkait dengan objek yang tidak digunakan. Ini akan menyertakan objek Anda, dan objek terkelola apa pun yang Anda gunakan (mis Bitmap dan DbConnection).

Jika orang itu lupa menelepon Dispose(), kita dapat masih simpan bacon mereka! Kami masih punya cara untuk menyebutnya untuk mereka: ketika pengumpul sampah akhirnya berhasil membebaskan (yaitu menyelesaikan) objek kami.

catatan: Pengumpul sampah akhirnya akan membebaskan semua objek yang dikelola.   Ketika itu terjadi, itu memanggil Finalize   metode pada objek. GC tidak tahu, atau   peduli, tentang anda  Membuang metode.   Itu hanya nama yang kami pilih   metode yang kami sebut saat kami ingin mendapatkannya   menyingkirkan barang yang tidak dikelola.

Penghancuran objek kami oleh kolektor Sampah adalah sempurna waktu untuk membebaskan sumber daya yang tidak dikelola itu. Kami melakukan ini dengan mengesampingkan Finalize() metode.

catatan: Dalam C #, Anda tidak secara eksplisit menimpa Finalize() metode.   Anda menulis metode itu seperti Sebuah C ++ destructor, dan   compiler mengambil itu menjadi implementasi Anda Finalize() metode:

~MyObject()
{
    //we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
    Dispose(); //<--Warning: subtle bug! Keep reading!
}

Tapi ada bug di kode itu. Anda lihat, pengumpul sampah berjalan di atas utas latar belakang; Anda tidak tahu urutan dua benda dihancurkan. Sangat mungkin bahwa dalam Anda Dispose() kode, yang berhasil objek yang Anda coba singkirkan (karena Anda ingin membantu) tidak lagi ada:

public void Dispose()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);

   //Free managed resources too
   if (this.databaseConnection != null)
   {
      this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
      this.databaseConnection = null;
   }
   if (this.frameBufferImage != null)
   {
      this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
      this.frameBufferImage = null;
   }
}

Jadi apa yang Anda butuhkan adalah suatu cara Finalize() untuk memberi tahu Dispose() itu seharusnya tidak menyentuh apa pun yang dikelola sumber daya (karena mereka mungkin tidak ada di sana lagi), sambil tetap membebaskan sumber daya yang tidak dikelola.

Pola standar untuk melakukan ini adalah memiliki Finalize() dan Dispose() keduanya memanggil a ketiga(!) metode; di mana Anda melewati pepatah Boolean jika Anda menyebutnya dari Dispose() (sebagai lawan Finalize()), artinya aman untuk membebaskan sumber daya yang dikelola.

Ini intern metode bisa diberikan beberapa nama arbitrer seperti "CoreDispose", atau "MyInternalDispose", tetapi adalah tradisi untuk menyebutnya Dispose(Boolean):

protected void Dispose(Boolean disposing)

Tetapi nama parameter yang lebih bermanfaat mungkin:

protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //Free managed resources too, but only if I'm being called from Dispose
   //(If I'm being called from Finalize then the objects might not exist
   //anymore
   if (itIsSafeToAlsoFreeManagedObjects)  
   {    
      if (this.databaseConnection != null)
      {
         this.databaseConnection.Dispose();
         this.databaseConnection = null;
      }
      if (this.frameBufferImage != null)
      {
         this.frameBufferImage.Dispose();
         this.frameBufferImage = null;
      }
   }
}

Dan Anda mengubah implementasi Anda IDisposable.Dispose() metode untuk:

public void Dispose()
{
   Dispose(true); //I am calling you from Dispose, it's safe
}

dan finalizer Anda ke:

~MyObject()
{
   Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
}

Catatan: Jika objek Anda turun dari objek yang diimplementasikan Dispose, maka jangan lupa untuk memanggil mereka mendasarkan Buang metode saat Anda mengganti Dispose:

public Dispose()
{
    try
    {
        Dispose(true); //true: safe to free managed resources
    }
    finally
    {
        base.Dispose();
    }
}

Dan semuanya bagus, kecuali Anda bisa lebih baik!


Jika panggilan pengguna Dispose() pada objek Anda, maka semuanya telah dibersihkan. Kemudian, ketika pengumpul sampah datang dan panggilan Selesai, maka akan menelepon Dispose lagi.

Tidak hanya ini sia-sia, tetapi jika objek Anda memiliki referensi sampah ke objek yang sudah Anda buang dari terakhir panggilan ke Dispose(), Anda akan mencoba membuangnya lagi!

Anda akan melihat di kode saya, saya berhati-hati untuk menghapus referensi ke objek yang saya sudah dibuang, jadi saya tidak mencoba untuk menelepon Dispose pada referensi objek sampah. Tapi itu tidak menghentikan bug halus dari merayap masuk.

Ketika panggilan pengguna Dispose(): pegangan CursorFileBitmapIconServiceHandle dihancurkan. Kemudian ketika pengumpul sampah berjalan, ia akan mencoba untuk menghancurkan pegangan yang sama lagi.

protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy 
   ...
}

Cara Anda memperbaikinya adalah memberitahu pengumpul sampah bahwa itu tidak perlu repot-repot menyelesaikan objek - sumber dayanya sudah dibersihkan, dan tidak ada lagi pekerjaan yang diperlukan. Anda melakukan ini dengan menelepon GC.SuppressFinalize() dalam Dispose() metode:

public void Dispose()
{
   Dispose(true); //I am calling you from Dispose, it's safe
   GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
}

Sekarang pengguna telah menelepon Dispose(), kita punya:

  • membebaskan sumber daya yang tidak dikelola
  • membebaskan sumber daya yang dikelola

Tidak ada titik di GC menjalankan finalizer - semuanya diurus.

Tidak dapatkah saya menggunakan Finalisasi untuk membersihkan sumber daya yang tidak dikelola?

Dokumentasi untuk Object.Finalize mengatakan:

Metode Finalize digunakan untuk melakukan operasi pembersihan pada sumber daya yang tidak dikelola yang dipegang oleh objek saat ini sebelum objek dihancurkan.

Tetapi dokumentasi MSDN juga mengatakan, untuk IDisposable.Dispose:

Melakukan tugas-tugas yang ditentukan aplikasi yang terkait dengan membebaskan, melepaskan, atau mengatur ulang sumber daya yang tidak dikelola.

Jadi, mana itu? Yang mana tempat bagi saya untuk membersihkan sumber daya yang tidak dikelola? Jawabannya adalah:

Itu pilihanmu! Tapi pilih Dispose.

Anda tentu bisa menempatkan pembersihan unmanaged Anda di finalizer:

~MyObject()
{
   //Free unmanaged resources
   Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);

   //A C# destructor automatically calls the destructor of its base class.
}

Masalahnya adalah Anda tidak tahu kapan pengumpul sampah akan berkeliling untuk menyelesaikan objek Anda. Sumber daya asli yang tidak dikelola, tidak dibutuhkan, dan tidak digunakan akan tetap ada sampai pengumpul sampah akhirnya berjalan. Maka itu akan memanggil metode finalizer Anda; membersihkan sumber daya yang tidak dikelola. Dokumentasi dari Object.Finalize menunjukkan ini:

Waktu yang tepat ketika finalis mengeksekusi tidak terdefinisi. Untuk memastikan rilis deterministik sumber daya untuk instance kelas Anda, terapkan Dekat metode atau menyediakan IDisposable.Dispose pelaksanaan.

Ini adalah keutamaan menggunakan Dispose untuk membersihkan sumber daya yang tidak dikelola; Anda harus mengetahui, dan mengontrol, ketika sumber daya yang tidak dikelola dibersihkan. Kehancuran mereka "deterministik".


Untuk menjawab pertanyaan asli Anda: Mengapa tidak merilis memori sekarang, daripada ketika GC memutuskan untuk melakukannya? Saya memiliki perangkat lunak pengenal wajah kebutuhan untuk menghilangkan 530 MB gambar internal sekarang, karena mereka tidak lagi dibutuhkan. Ketika kita tidak: mesin grinds ke swapping berhenti.

Pembacaan Bonus

Bagi siapa saja yang suka gaya jawaban ini (menjelaskan Mengapa, sehingga bagaimana menjadi jelas), saya sarankan Anda membaca Bab Satu dari COM Essential Don Box:

Dalam 35 halaman ia menjelaskan masalah menggunakan objek biner, dan menciptakan COM di depan mata Anda. Setelah Anda menyadari Mengapa dari COM, sisa 300 halaman sudah jelas, dan hanya detail implementasi Microsoft.

Saya pikir setiap programmer yang pernah berurusan dengan objek atau COM harus, paling tidak, membaca bab pertama. Ini adalah penjelasan terbaik tentang apa pun yang pernah ada.

Pembacaan Bonus Ekstra

Ketika semua yang Anda tahu salah oleh Eric Lippert

Oleh karena itu sangat sulit untuk menulis finalizer yang benar,   dan saran terbaik yang bisa saya berikan kepada Anda adalah jangan mencoba.


2286
2018-02-11 18:20



IDisposable sering digunakan untuk mengeksploitasi using pernyataan dan mengambil keuntungan dari cara mudah untuk melakukan pembersihan deterministik dari objek yang dikelola.

public class LoggingContext : IDisposable {
    public Finicky(string name) {
        Log.Write("Entering Log Context {0}", name);
        Log.Indent();
    }
    public void Dispose() {
        Log.Outdent();
    }

    public static void Main() {
        Log.Write("Some initial stuff.");
        try {
            using(new LoggingContext()) {
                Log.Write("Some stuff inside the context.");
                throw new Exception();
            }
        } catch {
            Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
        } finally {
            Log.Write("Some final stuff.");
        }
    }
}

54
2018-02-11 18:42



Tujuan pola Dispose adalah menyediakan mekanisme untuk membersihkan sumber daya yang dikelola dan tidak dikelola dan ketika itu terjadi tergantung pada bagaimana metode Dispose dipanggil. Dalam contoh Anda, penggunaan Dispose sebenarnya tidak melakukan apa pun yang terkait dengan pembuangan, karena menghapus daftar tidak berdampak pada koleksi yang dibuang. Demikian juga, panggilan untuk mengatur variabel ke null juga tidak berdampak pada GC.

Anda dapat melihat ini artikel untuk detail lebih lanjut tentang cara menerapkan pola Dispose, tetapi pada dasarnya terlihat seperti ini:

public class SimpleCleanup : IDisposable
{
    // some fields that require cleanup
    private SafeHandle handle;
    private bool disposed = false; // to detect redundant calls

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources.
                if (handle != null)
                {
                    handle.Dispose();
                }
            }

            // Dispose unmanaged managed resources.

            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Metode yang paling penting di sini adalah Dispose (bool), yang sebenarnya berjalan di bawah dua kondisi berbeda:

  • disposing == true: metode telah dipanggil secara langsung atau tidak langsung oleh kode pengguna. Sumber daya yang dikelola dan tidak dikelola dapat dibuang.
  • disposing == false: metode telah dipanggil oleh runtime dari dalam finalizer, dan Anda tidak perlu mereferensikan objek lain. Hanya sumber daya yang tidak dikelola dapat dibuang.

Masalah dengan membiarkan GC berhati-hati dalam melakukan pembersihan adalah bahwa Anda tidak memiliki kendali nyata kapan GC akan menjalankan siklus pengumpulan (Anda dapat memanggil GC.Collect (), tetapi Anda benar-benar tidak boleh) sehingga sumber daya dapat tetap sekitar lebih lama dari yang dibutuhkan. Ingat, memanggil Dispose () sebenarnya tidak menyebabkan siklus pengumpulan atau dengan cara apa pun menyebabkan GC mengumpulkan / membebaskan objek; itu hanya menyediakan sarana untuk lebih deterministik membersihkan sumber daya yang digunakan dan memberi tahu GC bahwa pembersihan ini telah dilakukan.

Inti dari pola IDisposable dan buang tidak tentang segera membebaskan memori. Satu-satunya waktu panggilan untuk Dispose akan benar-benar bahkan memiliki kesempatan untuk segera membebaskan memori adalah ketika menangani disposing == false scenario dan memanipulasi sumber daya yang tidak dikelola. Untuk kode yang dikelola, memori tidak akan benar-benar direklamasi sampai GC menjalankan siklus pengumpulan, yang benar-benar tidak Anda kendalikan (selain memanggil GC.Collect (), yang sudah saya sebutkan bukanlah ide yang bagus).

Skenario Anda tidak benar-benar valid karena string .NET tidak menggunakan sumber daya yang tidak terukur dan tidak menerapkan IDisposable, tidak ada cara untuk memaksa mereka untuk "dibersihkan."


36
2018-02-11 20:21



Seharusnya tidak ada lagi panggilan ke metode objek setelah Dispose telah dipanggil di atasnya (meskipun suatu objek harus mentolerir panggilan lebih lanjut ke Dispose). Karena itu contoh dalam pertanyaan itu konyol. Jika Dispose dipanggil, maka objek itu sendiri dapat dibuang. Jadi pengguna hanya harus membuang semua referensi ke seluruh objek (mengaturnya ke null) dan semua objek terkait internal itu akan secara otomatis dibersihkan.

Adapun pertanyaan umum tentang dikelola / tidak dikelola dan diskusi dalam jawaban lain, saya pikir jawaban untuk pertanyaan ini harus dimulai dengan definisi sumber daya yang tidak dikelola.

Apa intinya adalah bahwa ada fungsi yang dapat Anda panggil untuk menempatkan sistem ke dalam keadaan, dan ada fungsi lain yang dapat Anda hubungi untuk mengembalikannya dari keadaan itu. Sekarang, dalam contoh umum, yang pertama mungkin adalah fungsi yang mengembalikan file, dan yang kedua mungkin panggilan untuk CloseHandle.

Tapi - dan ini adalah kuncinya - mereka bisa menjadi sepasang fungsi yang cocok. Satu membangun sebuah negara, yang lain meruntuhkannya. Jika negara telah dibangun tetapi belum dirobohkan, maka sebuah instance dari sumber daya itu ada. Anda harus mengatur agar teardown terjadi pada waktu yang tepat - sumber daya tidak dikelola oleh CLR. Satu-satunya sumber daya yang dikelola secara otomatis adalah memori. Ada dua jenis: GC, dan tumpukan. Tipe nilai dikelola oleh stack (atau dengan menumpang di dalam tipe referensi), dan tipe referensi dikelola oleh GC.

Fungsi-fungsi ini dapat menyebabkan perubahan status yang dapat disisipkan dengan bebas, atau mungkin perlu disarangkan dengan sempurna. Perubahan status mungkin tidak aman, atau mungkin tidak.

Lihatlah contoh dalam pertanyaan Hakim. Perubahan pada lekukan file Log harus benar-benar bersarang, atau semuanya berjalan salah. Juga mereka tidak mungkin threadsafe.

Anda bisa menumpang kendaraan bersama pengumpul sampah untuk membersihkan sumber daya yang tidak dikelola. Tetapi hanya jika fungsi perubahan negara adalah threadsafe dan dua negara dapat memiliki masa hidup yang tumpang tindih dengan cara apa pun. Jadi contoh Keadilan dari sumber daya TIDAK harus memiliki finalizer! Itu tidak akan membantu siapa pun.

Untuk sumber daya semacam itu, Anda bisa menerapkan IDisposable, tanpa finalizer. Finalizer mutlak opsional - itu harus. Ini dipoles atau bahkan tidak disebutkan dalam banyak buku.

Anda kemudian harus menggunakan using pernyataan untuk memiliki kesempatan untuk memastikan hal itu Dispose disebut. Ini pada dasarnya seperti menumpang dengan tumpukan (jadi finalizer adalah GC, using adalah ke stack).

Bagian yang hilang adalah Anda harus menulis secara manual Buang dan buat panggilan ke bidang Anda dan kelas dasar Anda. Programer C ++ / CLI tidak perlu melakukan itu. Kompilator menulisnya untuk sebagian besar kasus.

Ada alternatif, yang saya lebih suka untuk menyatakan bahwa sarang sempurna dan tidak threadsafe (terlepas dari hal lain, menghindari suku cadang yang tidak masuk akal Anda masalah memiliki argumen dengan seseorang yang tidak bisa menolak menambahkan finalizer untuk setiap kelas yang mengimplementasikan IDisposable) .

Alih-alih menulis kelas, Anda menulis sebuah fungsi. Fungsi ini menerima delegasi untuk memanggil kembali ke:

public static void Indented(this Log log, Action action)
{
    log.Indent();
    try
    {
        action();
    }
    finally
    {
        log.Outdent();
    }
}

Dan kemudian contoh sederhananya adalah:

Log.Write("Message at the top");
Log.Indented(() =>
{
    Log.Write("And this is indented");

    Log.Indented(() =>
    {
        Log.Write("This is even more indented");
    });
});
Log.Write("Back at the outermost level again");

Lambda yang diteruskan berfungsi sebagai blok kode, jadi seperti Anda membuat struktur kontrol Anda sendiri untuk melayani tujuan yang sama using, kecuali bahwa Anda tidak lagi memiliki bahaya penelepon yang menyalahgunakannya. Tidak mungkin mereka gagal membersihkan sumber daya.

Teknik ini kurang berguna jika sumber daya adalah jenis yang mungkin memiliki masa hidup yang tumpang tindih, karena kemudian Anda ingin dapat membangun sumber daya A, kemudian sumber daya B, kemudian membunuh sumber daya A dan kemudian kemudian membunuh sumber daya B. Anda tidak dapat melakukan itu jika Anda memaksa pengguna untuk bersarang seperti ini. Tapi kemudian Anda perlu menggunakannya IDisposable (tetapi masih tanpa finalizer, kecuali Anda telah menerapkan keamanan benang, yang tidak gratis).


17
2018-02-11 19:31



Skenario Saya menggunakan IDisposable: membersihkan sumber daya yang tidak dikelola, berhenti berlangganan untuk acara, koneksi dekat

Idiom yang saya gunakan untuk menerapkan IDisposable (bukan threadsafe):

class MyClass : IDisposable {
    // ...

    #region IDisposable Members and Helpers
    private bool disposed = false;

    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing) {
        if (!this.disposed) {
            if (disposing) {
                // cleanup code goes here
            }
            disposed = true;
        }
    }

    ~MyClass() {
        Dispose(false);
    }
    #endregion
}

14
2018-02-11 18:20



Jika MyCollection akan menjadi sampah yang dikumpulkan, maka Anda tidak perlu membuangnya. Melakukan hal itu hanya akan menghasilkan CPU lebih dari yang diperlukan, dan bahkan mungkin membatalkan beberapa analisis pra-perhitungan yang telah dilakukan oleh garbage collector.

saya menggunakan IDisposable untuk melakukan hal-hal seperti memastikan utas dibuang dengan benar, bersama dengan sumber daya yang tidak dikelola.

EDIT Sebagai tanggapan atas komentar Scott:

Satu-satunya waktu metrik kinerja GC terpengaruh adalah ketika panggilan [sic] GC.Collect () dibuat "

Secara konseptual, GC mempertahankan pandangan grafik referensi objek, dan semua referensi dari grafik tumpukan frame. Tumpukan ini bisa sangat besar dan menjangkau banyak halaman memori. Sebagai pengoptimalan, GC menyimpan analisisnya terhadap halaman yang tidak sering berubah sangat sering untuk menghindari penyelarasan ulang halaman yang tidak perlu. GC menerima pemberitahuan dari kernel ketika data di halaman berubah, sehingga mengetahui bahwa halaman tersebut kotor dan membutuhkan pemindaian ulang. Jika koleksi berada di Gen0 maka kemungkinan hal-hal lain di halaman juga berubah, tetapi ini kurang mungkin di Gen1 dan Gen2. Anecdotally, kait ini tidak tersedia di Mac OS X untuk tim yang mem-porting GC ke Mac untuk mendapatkan plug-in Silverlight yang bekerja pada platform itu.

Poin lain terhadap pembuangan sumber daya yang tidak perlu: bayangkan situasi di mana proses sedang dibongkar. Bayangkan juga bahwa prosesnya telah berjalan selama beberapa waktu. Kemungkinan besar banyak halaman memori proses itu telah di-swap ke disk. Paling tidak mereka tidak lagi di L1 atau L2 cache. Dalam situasi seperti itu tidak ada gunanya untuk aplikasi yang membongkar semua data dan kode halaman kembali ke dalam memori untuk 'melepaskan' sumber daya yang akan dirilis oleh sistem operasi pula ketika proses berakhir. Ini berlaku untuk sumber daya terkelola dan bahkan tertentu yang tidak dikelola. Hanya sumber daya yang menjaga agar non-latar belakang tetap hidup harus dibuang, jika tidak, prosesnya akan tetap hidup.

Sekarang, selama eksekusi normal ada sumber daya fana yang harus dibersihkan dengan benar (seperti @fezmonkey menunjukkan koneksi database, soket, pegangan jendela) untuk menghindari kebocoran memori yang tidak terkelola. Ini adalah hal-hal yang harus dibuang. Jika Anda membuat beberapa kelas yang memiliki untaian (dan dengan memiliki maksud saya membuatnya memiliki dan karena itu bertanggung jawab untuk memastikannya berhenti, setidaknya oleh gaya pengkodean saya), maka kelas tersebut kemungkinan besar harus menerapkan IDisposable dan meruntuhkan benang selama Dispose.

Kerangka .NET menggunakan IDisposable antarmuka sebagai sinyal, bahkan peringatan, bagi para pengembang bahwa kelas ini harus dibuang. Saya tidak bisa memikirkan tipe apa pun dalam kerangka yang berlaku IDisposable (tidak termasuk implementasi antarmuka eksplisit) di mana pembuangan bersifat opsional.


11
2018-02-11 18:19



Ya, kode itu benar-benar berlebihan dan tidak perlu dan itu tidak membuat pengumpul sampah melakukan apa pun yang tidak akan dilakukan (sekali contoh MyCollection keluar dari ruang lingkup, yaitu.) Terutama .Clear() panggilan.

Jawaban untuk hasil edit Anda: Semacam. Jika saya melakukan ini:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has no Dispose() method
    instance.FillItWithAMillionStrings();
}

// 1 million strings are in memory, but marked for reclamation by the GC

Ini secara fungsional identik dengan ini untuk keperluan manajemen memori:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has your Dispose()
    instance.FillItWithAMillionStrings();
    instance.Dispose();
}

// 1 million strings are in memory, but marked for reclamation by the GC

Jika Anda benar-benar benar-benar perlu untuk membebaskan memori saat ini juga, panggil GC.Collect(). Tidak ada alasan untuk melakukan ini di sini. Memori akan dibebaskan saat dibutuhkan.


10
2018-06-03 21:07



Jika Anda menghendaki hapus sekarang juga, gunakan memori tidak dikelola.

Lihat:


7
2018-02-11 21:08



Saya tidak akan mengulangi hal-hal biasa tentang Menggunakan atau membebaskan sumber daya yang tidak dikelola, yang semuanya telah dibahas. Tetapi saya ingin menunjukkan apa yang tampaknya merupakan kesalahpahaman umum.
Diberikan kode berikut

Public Class LargeStuff
  Menerapkan IDisposable
  Private _Large sebagai string ()

  'Beberapa kode aneh yang berarti _Large sekarang berisi beberapa juta string panjang.

  Public Sub Dispose () Menerapkan IDisposable.Dispose
    _Large = Tidak ada
  Akhir Sub

Saya menyadari bahwa penerapan Disposable tidak mengikuti panduan saat ini, tetapi semoga Anda semua mendapatkan ide tersebut.
Sekarang, ketika Dispose dipanggil, berapa banyak memori yang dibebaskan?

Jawaban: Tidak ada.
Memanggil Buang dapat melepaskan sumber daya yang tidak dikelola, itu TIDAK BISA merebut kembali memori yang dikelola, hanya GC yang bisa melakukannya. Itu tidak berarti bahwa hal di atas bukanlah ide yang baik, mengikuti pola di atas masih merupakan ide yang bagus. Setelah Dispose dijalankan, tidak ada yang menghentikan GC untuk mengklaim kembali memori yang digunakan oleh _Large, meskipun instance LargeStuff masih dalam lingkup. String dalam _Large mungkin juga dalam gen 0 tetapi instance LargeStuff mungkin gen 2, jadi sekali lagi, memori akan diklaim ulang lebih cepat.
Tidak ada gunanya menambahkan finaliser untuk memanggil metode Dispose yang ditampilkan di atas. Itu hanya akan MENGHAPUS kembali mengklaim memori untuk memungkinkan finaliser untuk menjalankan.


6
2018-02-11 21:07



Dalam contoh yang Anda posting, masih belum "membebaskan memori sekarang". Semua memori adalah sampah yang dikumpulkan, tetapi memungkinkan memori untuk dikumpulkan lebih awal generasi. Anda harus menjalankan beberapa tes untuk memastikan.


Pedoman Perancangan Kerangka adalah pedoman, dan bukan aturan. Mereka memberi tahu Anda apa antarmuka terutama untuk, kapan menggunakannya, bagaimana menggunakannya, dan kapan tidak menggunakannya.

Saya pernah membaca kode yang sederhana RollBack () pada kegagalan memanfaatkan IDisposable. Kelas MiniTx di bawah ini akan memeriksa bendera di Dispose () dan jika Commit panggilan tidak pernah terjadi maka akan memanggilnya Rollback pada dirinya sendiri. Ini menambahkan lapisan tipuan membuat kode panggilan jauh lebih mudah untuk dipahami dan dipelihara. Hasilnya tampak seperti:

using( MiniTx tx = new MiniTx() )
{
    // code that might not work.

    tx.Commit();
} 

Saya juga melihat kode timing / logging melakukan hal yang sama. Dalam hal ini metode Dispose () menghentikan timer dan mencatat bahwa blok telah keluar.

using( LogTimer log = new LogTimer("MyCategory", "Some message") )
{
    // code to time...
}

Jadi di sini adalah beberapa contoh konkret yang tidak melakukan pembersihan sumber daya yang tidak dikelola, tetapi berhasil digunakan IDisposable untuk membuat kode bersih.


5
2018-02-11 20:32