membatalkan alokasi memori untuk objek yang belum saya setel ke nol

EDIT: Masalah tidak ada hubungannya dengan pertanyaan. Memang ada yang salah dengan kode saya, dan sebenarnya sangat sederhana sehingga saya tidak ingin menaruhnya di internet. Terima kasih.

Saya membaca sekitar 550k catatan direktori aktif dan menyimpannya dalam Daftar, kelas menjadi pembungkus sederhana untuk pengguna AD. Saya kemudian membagi daftar ADRecords menjadi empat daftar, masing-masing berisi seperempat dari total. Setelah saya melakukan ini, saya membaca sekitar 400 ribu catatan dari database, yang dikenal sebagai catatan EDR, ke dalam DataTable. Saya mengambil empat perempat dari daftar saya dan menelurkan empat utas, melewati masing-masing dari empat perempat. Saya harus mencocokkan data AD dengan data EDR menggunakan email sekarang, namun kami berencana menambahkan lebih banyak hal untuk dicocokkan nanti.

Saya memiliki foreach pada daftar data AD, dan di dalamnya, saya harus menjalankan perulangan for pada data EDR untuk memeriksa masing-masing data, karena jika data AD cocok dengan lebih dari satu data EDR, maka itu bukan a pertandingan langsung, dan tidak boleh diperlakukan sebagai pertandingan langsung.

Masalah saya, pada saat saya masuk ke daftar ini, daftar ADRecords saya hanya memiliki sekitar 130 catatan di dalamnya, tetapi tepat setelah saya menarik semuanya, saya Console.WriteLine menghitungnya, dan jumlahnya 544k.

Saya mulai berpikir bahwa meskipun saya belum menyetel daftar ke null untuk dikumpulkan nanti, C# atau Windows atau sesuatu sebenarnya mengambil daftar saya untuk memberi ruang bagi catatan EDR karena saya belum menggunakan daftar itu di a ketika. Basis data yang harus saya gunakan untuk membaca catatan EDR adalah server tertaut, jadi dibutuhkan sekitar 10 menit untuk membaca semuanya, jadi daftar saya sebenarnya menganggur selama 10 menit, tetapi tidak pernah disetel ke nol.

Ada ide?

//splitting list and passing in values to threads.
List<ADRecord> adRecords = GetAllADRecords();
        for (int i = 0; i < adRecords.Count/4; i++)
        {
            firstQuarter.Add(adRecords[i]);
        }
        for (int i = adRecords.Count/4; i < adRecords.Count/2; i++)
        {
            secondQuarter.Add(adRecords[i]);
        }
        for (int i = adRecords.Count/2; i < (adRecords.Count/4)*3; i++)
        {
            thirdQuarter.Add(adRecords[i]);
        }
        for (int i = (adRecords.Count/4)*3; i < adRecords.Count; i++)
        {
            fourthQuarter.Add(adRecords[i]);
        }
        DataTable edrRecordsTable = GetAllEDRRecords();

        DataRow[] edrRecords = edrRecordsTable.Select("Email_Address is not null and Email_Address <> ''", "Email_Address");
        Dictionary<string, int> letterPlaces = FindLetterPlaces(edrRecords);
        Thread one = new Thread(delegate() { ProcessMatches(firstQuarter, edrRecords, letterPlaces); });
        Thread two = new Thread(delegate() { ProcessMatches(secondQuarter, edrRecords,  letterPlaces); });
        Thread three = new Thread(delegate() { ProcessMatches(thirdQuarter, edrRecords,  letterPlaces); });
        Thread four = new Thread(delegate() { ProcessMatches(fourthQuarter, edrRecords, letterPlaces); });
        one.Start();
        two.Start();
        three.Start();
        four.Start();

Di ProcessMatches, ada foreach pada Daftar ADRecords yang diteruskan. Baris pertama di foreach adalah AdRecordsProcessed++; yang merupakan int statis global, dan program berakhir pada 130, bukan 544k.


person seekerOfKnowledge    schedule 08.10.2010    source sumber
comment
GC dengan tegas tidak melakukan hal itu. Celakalah dunia jika GC mengumpulkan benda-benda bekas.   -  person Kirk Woll    schedule 08.10.2010
comment
Meragukan kode Anda, bukan alatnya adalah awal yang baik saat mendiagnosis masalah seperti itu. Contoh kode singkat namun lengkap yang menunjukkan masalah ini akan sangat membantu.   -  person LBushkin    schedule 08.10.2010
comment
Lupa menyebutkan, saya pernah menambahkan Console.WriteLine setelah saya membaginya, memeriksa jumlah masing-masing dari empat, dan menjumlahkannya, ternyata sama dengan totalnya.   -  person seekerOfKnowledge    schedule 08.10.2010
comment
Perusahaan macam apa yang mempunyai setengah juta karyawan?   -  person Hans Passant    schedule 08.10.2010
comment
@Hans Passant Indian Railways memiliki 1,6 juta karyawan :) walaupun saya ragu mereka menggunakan AD...   -  person AakashM    schedule 08.10.2010
comment
Komentar terakhir, dan jika ini tidak menghasilkan ide tambahan, maka saya akan mengumpulkan seluruh kantor saya untuk sesi curah pendapat. Ketika saya menarik EDR dari server lokal, tidak tertaut, ia melakukannya dengan cepat, sekitar satu setengah menit, dan SEMUA catatan AD saya disimpan di memori, tetapi tidak demikian halnya dengan server tertaut, yang memakan waktu sekitar 10 menit.   -  person seekerOfKnowledge    schedule 08.10.2010
comment
@Hans - Pemerintah Federal? :)   -  person Bryan    schedule 08.10.2010
comment
@Bryan - sulit membayangkan mereka terorganisir :) Wal-mart adalah yang terbesar dengan 2,1 juta. Namun masalahnya sama dengan perkeretaapian India, tidak banyak karyawannya yang memiliki desktop. Saya kira Exxon Mobil atau sejenisnya. Atau hanya data uji palsu.   -  person Hans Passant    schedule 08.10.2010
comment
@Hans - Kliennya tidak begitu terorganisir, dan fakta itulah yang menjadi alasan saya harus melakukan program ini.   -  person seekerOfKnowledge    schedule 09.10.2010


Jawaban (4)


Variabel tidak pernah disetel ke nol dan masih dalam cakupan? Jika demikian, itu tidak boleh dikumpulkan dan waktu menganggur bukanlah masalah Anda.

Masalah pertama yang saya lihat adalah:

AdRecordsProcessed++; 

Apakah Anda mengunci variabel global tersebut sebelum memperbaruinya? Jika tidak, dan bergantung pada seberapa cepat data diproses, hasilnya akan lebih rendah dari yang Anda harapkan.

Coba jalankan dari satu thread (yaitu meneruskan adRecords alih-alih firstQuarter dan jangan memulai thread lainnya.) Apakah ini berfungsi seperti yang diharapkan dengan 1 thread?

person Kendrick    schedule 08.10.2010
comment
Apa yang dilakukan seseorang dengan jawaban seperti ini? :) Sepertinya Anda mengajukan pertanyaan kepada OP -- yaitu komentar. - person Kirk Woll; 08.10.2010
comment
Pertanyaan saya sudah terjawab di postingan sekarang, tapi Anda benar, bagian kedua dari jawaban saya seharusnya berupa komentar. - person Kendrick; 08.10.2010
comment
@jalf babak pertama adalah jawabannya. waktu menganggur bukanlah masalah Anda. Saya sudah membiarkannya di tempatnya. - person Kendrick; 08.10.2010
comment
Menanggapi masalah pertama Anda, Anda tidak dapat mengunci int. perintah kunci memerlukan tipe referensi. - person seekerOfKnowledge; 12.10.2010
comment
Saya belum mencobanya, tetapi saya masih menganggap operasi ++ tidak aman untuk thread dan oleh karena itu mungkin menjadi masalah. Kunci blok kode yang mengubah variabel dan Anda telah mengunci variabel secara efektif. Apa pun yang terjadi, saya akan menjalankannya dengan satu utas (mungkin pada kumpulan baris yang dikurangi) dan melihat apakah ia berfungsi di sana. Jika demikian, threading mungkin menjadi masalah Anda. - person Kendrick; 12.10.2010
comment
objek hanya baca statis _locker = objek baru(); dengan ini di tingkat kelas, saya dapat menguncinya di sekitar operasi ++ saya, dan angka saya lebih akurat. Terima kasih. - person seekerOfKnowledge; 13.10.2010
comment
Di .NET 4.0, ada objek kelas Interlocked yang merupakan praktik yang jauh lebih baik untuk digunakan daripada komentar saya sebelumnya. Kelas Interlocked diimplementasikan pada prosesor dan diekspos di .NET. Sangat keren - person seekerOfKnowledge; 23.11.2010

Pertama, Anda tidak menyetel daftar ke nol. Apa yang mungkin Anda lakukan adalah menyetel setiap referensi ke suatu daftar ke null (atau ke daftar lain), atau semua referensi tersebut mungkin akan keluar dari cakupan. Ini mungkin tampak seperti sebuah hal yang rewel, namun jika Anda harus memeriksa apa yang terjadi pada data Anda, inilah saatnya untuk tidak terlalu rewel dalam hal tersebut.

Kedua, membuat GC membatalkan alokasi sesuatu yang memiliki referensi langsung cukup sulit dilakukan. Anda dapat memalsukannya dengan WeakReference‹> atau mengira Anda telah menemukannya saat Anda menemukan bug di finaliser (karena referensi tersebut tidak benar-benar aktif, dan bahkan itu adalah masalah finaliser yang mencoba menangani yang sudah diselesaikan. daripada objek yang tidak dialokasikan). Bug dapat terjadi di mana saja, tetapi kemungkinan besar Anda telah menemukan cara untuk membuat GC membatalkan alokasi sesuatu yang aktif.

GC kemungkinan akan melakukan dua hal dengan daftar Anda:

  1. Kemungkinan besar akan memadatkan memori yang digunakannya, yang akan memindahkan item-item komponennya.
  2. Sangat mungkin untuk mempromosikannya ke generasi yang lebih tinggi.

Tak satu pun dari ini akan memiliki perubahan apa pun yang akan Anda deteksi kecuali Anda benar-benar mencarinya (tentu saja Anda akan melihat perubahan dalam generasi jika Anda terus memanggil GetGeneration(), tetapi selain itu Anda tidak akan benar-benar melakukannya).

Memori yang digunakan juga bisa dikeluarkan, tetapi akan dimasukkan kembali saat Anda menggunakan objek tersebut. Sekali lagi, tidak ada efek yang akan Anda sadari.

Terakhir, jika GC membatalkan alokasi sesuatu, jumlah item Anda tidak akan berkurang, Anda akan mengalami crash, karena jika objek baru saja dibatalkan alokasinya, sistem akan tetap mencoba menggunakan referensi yang seharusnya langsung ke objek tersebut.

Jadi, meskipun GC atau OS mungkin melakukan sesuatu untuk memberi ruang bagi objek Anda yang lain, itu bukanlah sesuatu yang dapat diamati dalam kode, dan tidak menghentikan ketersediaan objek dan dalam status program yang sama.

Ada hal lain yang menjadi masalahnya.

person Jon Hanna    schedule 08.10.2010
comment
Ini hampir layak mendapat suara negatif. GC hampir pasti tidak menjadi masalah di sini, dan jawaban Anda menunjukkan bahwa hal itu mungkin terjadi. - person John Saunders; 08.10.2010
comment
@John, di mana saya menyarankan bahwa GC mungkin menyebabkan masalah? Saya telah menjelaskan bagaimana hal-hal yang dilakukan GC tidak akan membatalkan alokasi apa pun, dan jika hal itu terjadi, hal itu akan menyebabkan kerusakan, bukan pengurangan item, sebelum mengatakan Ada masalah lain. Apa yang menunjukkan bahwa GC adalah masalahnya? - person Jon Hanna; 10.10.2010
comment
OP sedang mencari masalah dengan kodenya, dan menyarankan bahwa masalahnya mungkin pada GC. Hampir pasti bukan itu masalahnya, jadi saya rasa Anda seharusnya tidak menyebutkan GC dalam jawaban Anda. - person John Saunders; 13.10.2010
comment
@John. Karena saya tidak tahu apa masalahnya sebenarnya, cukup sulit untuk tidak menyebutkan GC dalam menjelaskan mengapa mungkin bukan masalahnya. - person Jon Hanna; 13.10.2010

Apakah ada alasan Anda harus mendapatkan semua data sekaligus? Jika Anda membagi data menjadi beberapa bagian, data tersebut akan lebih mudah dikelola. Yang saya tahu adalah harus masuk ke GC agak bau. Yang terbaik adalah melihat pemfaktoran ulang kode Anda.

person Wix    schedule 08.10.2010
comment
Saya rasa inilah yang seharusnya terjadi. Saya akan menambahkan komentar terakhir pada pertanyaan tersebut dengan memberikan detail lain yang mungkin membuat bingung sebagian orang, tetapi siapa yang tahu. - person seekerOfKnowledge; 08.10.2010

Pengumpul sampah tidak akan mengumpulkan:

  • Variabel global
  • Objek yang dikelola oleh objek statis
  • Variabel lokal
  • Variabel yang dapat direferensikan dengan metode apa pun di tumpukan panggilan

Jadi jika Anda bisa merujuknya dari kode Anda, tidak ada kemungkinan bahwa pemulung mengumpulkannya. Tidak mungkin, tidak bagaimana caranya.

Agar kolektor bisa mengoleksinya, semua referensi tentangnya pasti sudah hilang. Dan jika Anda bisa melihatnya, pastinya bukan itu masalahnya.

person Mike Hofer    schedule 08.10.2010