การจัดสรรหน่วยความจำใหม่สำหรับวัตถุที่ฉันไม่ได้ตั้งค่าเป็นโมฆะ

แก้ไข: ปัญหาไม่เกี่ยวข้องกับคำถาม มันมีบางอย่างผิดปกติกับโค้ดของฉันจริงๆ และจริงๆ แล้ว มันง่ายมากจนฉันไม่อยากเอามันไปไว้บนอินเทอร์เน็ต ขอบคุณนะ

ฉันอ่านบันทึก Active Directory ประมาณ 550,000 รายการและจัดเก็บไว้ในรายการ ซึ่งเป็นคลาสที่ห่อหุ้มอย่างง่ายสำหรับผู้ใช้ AD จากนั้น ฉันแบ่งรายการ ADRecords ออกเป็นสี่รายการ โดยแต่ละรายการมีหนึ่งในสี่ของทั้งหมด หลังจากที่ฉันทำสิ่งนี้ ฉันจะอ่านบันทึกประมาณ 400,000 รายการจากฐานข้อมูลที่เรียกว่าบันทึก EDR ลงใน DataTable ฉันเอาสี่ในสี่ของรายการของฉันและวางไข่สี่เธรดโดยผ่านแต่ละหนึ่งในสี่ในสี่ ฉันต้องจับคู่บันทึก AD กับบันทึก EDR โดยใช้อีเมลในขณะนี้ แต่เราวางแผนที่จะเพิ่มสิ่งต่างๆ เพื่อจับคู่ในภายหลัง

ฉันมี foreach ในรายการบันทึก AD และภายในนั้น ฉันต้องเรียกใช้ for loop บนบันทึก EDR เพื่อตรวจสอบแต่ละรายการ เพราะหากบันทึก AD ตรงกับบันทึก EDR มากกว่าหนึ่งรายการ นั่นไม่ใช่ การจับคู่โดยตรง และไม่ควรถือว่าเป็นการจับคู่โดยตรง

ปัญหาของฉัน เมื่อมาถึงหน้าหน้านี้ในรายการ ADRecords ของฉันมีเพียงประมาณ 130 รายการเท่านั้น แต่หลังจากที่ฉันดึงทั้งหมดเข้ามา ฉัน Console.WriteLine นับจำนวน และมันคือ 544k

ฉันเริ่มคิดว่าแม้ว่าฉันจะไม่ได้ตั้งค่ารายการเป็นโมฆะเพื่อรวบรวมในภายหลัง แต่ C# หรือ Windows หรือบางสิ่งบางอย่างกำลังนำรายการของฉันออกไปเพื่อให้มีที่ว่างสำหรับบันทึก EDR เนื่องจากฉันไม่ได้ใช้รายการใน ในขณะที่. ฐานข้อมูลที่ฉันต้องใช้เพื่ออ่านบันทึก EDR นั้นเป็นเซิร์ฟเวอร์ที่เชื่อมโยง ดังนั้นจึงใช้เวลาประมาณ 10 นาทีในการอ่านทั้งหมด ดังนั้นรายการของฉันจึงไม่ได้ใช้งานจริงเป็นเวลา 10 นาที แต่ไม่เคยตั้งค่าเป็นค่าว่าง

มีความคิดอะไรบ้าง?

//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();

ใน ProcessMatches มี foreach ในรายการ ADRecords ที่ส่งเข้ามา บรรทัดแรกใน foreach คือ AdRecordsProcessed++; ซึ่งเป็น int แบบคงที่ทั่วโลก และโปรแกรมจบที่ 130 แทนที่จะเป็น 544k


person seekerOfKnowledge    schedule 08.10.2010    source แหล่งที่มา
comment
กปปส.ไม่ได้ทำแบบนั้นเด็ดขาด วิบัติแก่โลกหาก GC รวบรวมสิ่งของที่ใช้งานอยู่   -  person Kirk Woll    schedule 08.10.2010
comment
สงสัยว่ารหัสของคุณไม่ใช่เครื่องมือ เป็นจุดเริ่มต้นที่ดีในการวินิจฉัยปัญหาดังกล่าว ตัวอย่างโค้ดสั้นๆ แต่ครบถ้วนที่แสดงให้เห็นถึงปัญหาจะเป็นประโยชน์   -  person LBushkin    schedule 08.10.2010
comment
ลืมบอกไปว่าครั้งหนึ่งฉันเคยเพิ่ม Console.WriteLine หลังจากที่แยกมันออก ตรวจสอบจำนวนในแต่ละสี่รายการ แล้วรวมเข้าด้วยกัน จริงๆ แล้วเท่ากับผลรวมทั้งหมด   -  person seekerOfKnowledge    schedule 08.10.2010
comment
บริษัทแบบไหนที่มีพนักงานครึ่งล้านคน?   -  person Hans Passant    schedule 08.10.2010
comment
@Hans Passant Indian Railways มีพนักงาน 1.6 ล้านคน :) แม้ว่าฉันสงสัยว่าพวกเขาใช้ AD...   -  person AakashM    schedule 08.10.2010
comment
ความคิดเห็นสุดท้าย และหากไม่ทำให้เกิดแนวคิดใดๆ เพิ่มเติม ฉันก็ขอรวบรวมทั้งออฟฟิศเพื่อระดมความคิด เมื่อฉันดึง EDR จากเซิร์ฟเวอร์ภายในเครื่องที่ไม่ได้เชื่อมโยง มันจะทำอย่างรวดเร็วประมาณหนึ่งนาทีครึ่ง และบันทึก AD ทั้งหมดของฉันจะถูกเก็บไว้ในหน่วยความจำ แต่นั่นไม่ใช่กรณีของเซิร์ฟเวอร์ที่เชื่อมโยง ซึ่งใช้เวลาประมาณ 10 นาที นาที.   -  person seekerOfKnowledge    schedule 08.10.2010
comment
@Hans - รัฐบาลกลาง? :)   -  person Bryan    schedule 08.10.2010
comment
@Bryan - ยากที่จะจินตนาการว่าพวกเขาเป็นระบบ :) Wal-mart ใหญ่ที่สุดด้วยจำนวน 2.1 ล้านคน แต่ปัญหาเดียวกับการรถไฟอินเดีย พนักงานไม่มากนักที่มีเดสก์ท็อป ฉันเดาว่าที่ Exxon Mobil หรือญาติของมัน หรือเพียงข้อมูลการทดสอบปลอม   -  person Hans Passant    schedule 08.10.2010
comment
@Hans - ลูกค้าไม่ได้รับการจัดระเบียบและความจริงก็คือสาเหตุที่ฉันต้องทำโปรแกรมนี้   -  person seekerOfKnowledge    schedule 09.10.2010


คำตอบ (4)


ตัวแปรไม่เคยถูกตั้งค่าเป็น null และยังอยู่ในขอบเขตใช่ไหม หากเป็นเช่นนั้น ก็ไม่ควรถูกรวบรวม และเวลาว่างไม่ใช่ปัญหาของคุณ

ปัญหาแรกที่ฉันเห็นคือ:

AdRecordsProcessed++; 

คุณกำลังล็อกตัวแปรโกลบอลนั้นก่อนที่จะอัพเดตหรือไม่? หากไม่เป็นเช่นนั้น และขึ้นอยู่กับความเร็วในการประมวลผลบันทึก ก็จะต่ำกว่าที่คุณคาดไว้

ลองเรียกใช้จากเธรดเดียว (เช่น ส่งผ่านใน adRecords แทนที่จะเป็น firstQuarter และอย่าเริ่มเธรดอื่น) มันทำงานตามที่คาดไว้กับ 1 เธรดหรือไม่

person Kendrick    schedule 08.10.2010
comment
เราจะทำอย่างไรกับคำตอบเช่นนี้? :) ดูเหมือนว่าคุณกำลังถามคำถาม OP - เช่นความคิดเห็น - person Kirk Woll; 08.10.2010
comment
คำถามของฉันได้รับคำตอบแล้วในโพสต์ตอนนี้ แต่คุณพูดถูก ครึ่งหลังของคำตอบของฉันควรเป็นความคิดเห็น - person Kendrick; 08.10.2010
comment
@jalf ครึ่งแรกคือคำตอบ เวลาว่างไม่ใช่ปัญหาของคุณ ฉันทิ้งสิ่งนั้นไว้แล้ว - person Kendrick; 08.10.2010
comment
เพื่อตอบสนองต่อปัญหาแรกของคุณ คุณไม่สามารถล็อค ints ได้ คำสั่ง lock จำเป็นต้องมีประเภทการอ้างอิง - person seekerOfKnowledge; 12.10.2010
comment
ฉันไม่ได้ลอง แต่ฉันยังคงถือว่าการดำเนินการ ++ ไม่ปลอดภัยสำหรับเธรดและอาจเป็นปัญหา ล็อกบล็อกของโค้ดที่แก้ไขตัวแปร จากนั้นคุณจะล็อกตัวแปรได้อย่างมีประสิทธิภาพ ไม่ว่าจะด้วยวิธีใด ฉันจะรันมันด้วยเธรดเดียว (อาจเป็นในชุดแถวที่ลดลง) และดูว่ามันทำงานอยู่ที่นั่นหรือไม่ หากเป็นเช่นนั้น การทำเกลียวอาจเป็นปัญหาของคุณ - person Kendrick; 12.10.2010
comment
วัตถุแบบอ่านอย่างเดียวแบบคงที่ _locker = วัตถุใหม่ (); ด้วยสิ่งนี้ในระดับชั้นเรียน ฉันสามารถล็อคการทำงาน ++ ของฉันได้ และตัวเลขของฉันก็แม่นยำยิ่งขึ้น ขอบคุณ - person seekerOfKnowledge; 13.10.2010
comment
ใน .NET 4.0 มีอ็อบเจ็กต์คลาส Interlocked ซึ่งเป็นวิธีปฏิบัติที่ดีกว่าความคิดเห็นก่อนหน้าของฉันมาก คลาส Interlocked ถูกนำไปใช้บนโปรเซสเซอร์และเปิดเผยใน .NET เจ๋งมาก - person seekerOfKnowledge; 23.11.2010

ประการแรก คุณไม่ได้ตั้งค่ารายการเป็นโมฆะ สิ่งที่คุณอาจทำคือตั้งค่าทุกการอ้างอิงไปยังรายการเป็นโมฆะ (หรือไปยังรายการอื่น) ไม่เช่นนั้นการอ้างอิงทั้งหมดอาจอยู่นอกขอบเขต นี่อาจดูเหมือนเป็นจุดจู้จี้จุกจิก แต่ถ้าคุณต้องตรวจสอบสิ่งที่เกิดขึ้นกับข้อมูลของคุณ ก็ถึงเวลาที่จะต้องจู้จี้จุกจิกกับสิ่งเหล่านี้

ประการที่สอง การให้ GC จัดสรรคืนบางสิ่งที่มีการอ้างอิงสดนั้นค่อนข้างทำได้ยาก คุณสามารถปลอมแปลงมันด้วย WeakReference‹> หรือคิดว่าคุณพบมันแล้วเมื่อคุณพบจุดบกพร่องใน Finaliser (เพราะว่าการอ้างอิงนั้นไม่ได้แสดงอยู่จริง และถึงแม้จะเป็นเรื่องของผู้เข้ารอบสุดท้ายที่พยายามจัดการกับการสรุปผลก็ตาม กว่าวัตถุที่ถูกจัดสรรคืน) จุดบกพร่องสามารถเกิดขึ้นได้ทุกที่ แต่คุณได้พบวิธีที่จะทำให้ GC จัดสรรคืนบางสิ่งที่มีอยู่แล้วนั้นไม่น่าเป็นไปได้อย่างยิ่ง

GC มีแนวโน้มที่จะดำเนินการสองสิ่งกับรายการของคุณ:

  1. มีแนวโน้มค่อนข้างที่จะกระชับหน่วยความจำที่ใช้ ซึ่งจะย้ายส่วนประกอบต่างๆ ไปรอบๆ
  2. มีแนวโน้มที่จะส่งเสริมให้คนรุ่นที่สูงขึ้นได้

สิ่งเหล่านี้จะไม่มีการเปลี่ยนแปลงใด ๆ ที่คุณจะตรวจพบเว้นแต่ว่าคุณจะมองหามันจริง ๆ (เห็นได้ชัดว่าคุณจะสังเกตเห็นการเปลี่ยนแปลงในรุ่นหากคุณเรียก GetGeneration() ต่อไป แต่นอกเหนือจากนั้นคุณจะไม่ได้ทำจริงๆ )

หน่วยความจำที่ใช้สามารถเพจออกได้ แต่จะถูกเพจกลับเข้าไปเมื่อคุณใช้ออบเจ็กต์ อีกครั้งคุณจะไม่สังเกตเห็นผลกระทบใด ๆ

สุดท้ายนี้ ถ้า GC ทำการจัดสรรคืนบางสิ่งบางอย่าง คุณจะไม่มีจำนวนรายการลดลง คุณจะเกิดข้อผิดพลาด เพราะหากอ็อบเจ็กต์เพิ่งถูกจัดสรรคืน ระบบจะยังคงพยายามใช้การอ้างอิงที่ใช้งานอยู่ตามที่คาดคะเนกับวัตถุเหล่านั้น

ดังนั้น แม้ว่า GC หรือ OS อาจทำบางอย่างเพื่อให้มีที่ว่างสำหรับอ็อบเจ็กต์อื่นของคุณ แต่ก็ไม่ใช่สิ่งที่สังเกตได้ในโค้ด และไม่ได้หยุดไม่ให้ออบเจ็กต์พร้อมใช้งานและอยู่ในสถานะทางโปรแกรมเดียวกัน

สิ่งอื่นที่เป็นปัญหา

person Jon Hanna    schedule 08.10.2010
comment
นี่เกือบจะคุ้มค่ากับการโหวตลง GC แทบไม่เป็นปัญหาที่นี่เลย และคำตอบของคุณก็บ่งบอกว่าอาจเป็นได้ - person John Saunders; 08.10.2010
comment
@John ฉันจะแนะนำที่ไหนว่า GC อาจทำให้เกิดปัญหาได้ที่ไหน ฉันได้อธิบายว่าสิ่งที่ GC จะทำจะไม่จัดสรรคืนสิ่งใด และหากทำเช่นนั้น มันจะทำให้เกิดความผิดพลาดมากกว่าการลดไอเทมลง ก่อนที่จะพูดว่า มีอย่างอื่นคือปัญหา อะไรบ่งบอกว่า GC เป็นปัญหา? - person Jon Hanna; 10.10.2010
comment
OP กำลังมองหาปัญหาเกี่ยวกับรหัสของเขา และแนะนำว่าปัญหาอาจอยู่ที่ GC เกือบจะไม่เป็นเช่นนั้นอย่างแน่นอน ดังนั้นฉันจึงรู้สึกว่าคุณไม่ควรพูดถึง GC ในคำตอบของคุณ - person John Saunders; 13.10.2010
comment
@จอห์น. เนื่องจากผมไม่รู้ว่าจริงๆ แล้วปัญหาคืออะไร การไม่พูดถึง GC ในการอธิบายว่าทำไมจึงไม่ใช่ปัญหาจึงค่อนข้างยาก - person Jon Hanna; 13.10.2010

มีเหตุผลที่คุณต้องรับข้อมูลทั้งหมดพร้อมกันหรือไม่? หากคุณแบ่งข้อมูลออกเป็นส่วนๆ ก็ควรจะจัดการได้ง่ายขึ้น ฉันรู้แค่ว่าต้องเข้าไปในของ GC มีกลิ่นเหม็นนิดหน่อย วิธีที่ดีที่สุดในการดูการปรับโครงสร้างโค้ดของคุณใหม่

person Wix    schedule 08.10.2010
comment
ฉันคิดว่ามันจะต้องเป็นเช่นนั้น ฉันจะเพิ่มความคิดเห็นสุดท้ายให้กับคำถามโดยให้รายละเอียดอื่นที่อาจทำให้บางคนสะดุด แต่ใครจะรู้ - person seekerOfKnowledge; 08.10.2010

คนเก็บขยะจะไม่เก็บ:

  • ตัวแปรโกลบอล
  • ออบเจ็กต์ที่ได้รับการจัดการโดยออบเจ็กต์แบบคงที่
  • ตัวแปรท้องถิ่น
  • ตัวแปรที่สามารถอ้างอิงได้โดยวิธีการใดๆ บน call stack

ดังนั้นหากคุณสามารถอ้างอิงจากโค้ดของคุณได้ ก็ไม่มีทางเป็นไปได้ที่ตัวรวบรวมขยะจะรวบรวมมัน ไม่มีทาง ไม่มีทาง

เพื่อให้นักสะสมรวบรวมได้ การอ้างอิงทั้งหมดจะต้องหายไป และถ้าคุณมองเห็นมัน มันก็ไม่เป็นเช่นนั้นอย่างแน่นอน

person Mike Hofer    schedule 08.10.2010