กลยุทธ์และเครื่องมือใดที่มีประโยชน์สำหรับการค้นหาหน่วยความจำรั่วใน. NET


152

ฉันเขียน C ++ เป็นเวลา 10 ปี ฉันพบปัญหาหน่วยความจำ แต่สามารถแก้ไขได้ด้วยความพยายามพอสมควร

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

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

กลยุทธ์และเครื่องมือชนิดใดที่มีประโยชน์สำหรับการแก้ไขปัญหาการรั่วไหลของหน่วยความจำใน. NET


ชื่อโพสต์ของคุณไม่ตรงกับคำถามในโพสต์ของคุณ ฉันขอแนะนำให้คุณอัปเดตชื่อของคุณ
เควิน

คุณถูก. ขออภัยฉันรู้สึกเบื่อหน่ายกับกระแสรั่วไหลที่ฉันกำลังตามล่าอยู่! อัปเดตชื่อแล้ว
Scott Langham

3
@Scott: อย่าเบื่อหน่ายกับ. NET มันไม่ใช่ปัญหา รหัสของคุณคือ
GEOCHET

3
ใช่รหัสของฉันหรือห้องสมุดบุคคลที่สามฉันมีความสุขในการใช้
Scott Langham

@Scott: ดูคำตอบของฉัน MemProfiler คุ้มค่า การใช้มันจะทำให้คุณมีความเข้าใจในระดับใหม่ของโลก. NET GC
GEOCHET

คำตอบ:


51

ฉันใช้MemProfilerของ Scitech เมื่อฉันสงสัยว่ามีหน่วยความจำรั่ว

จนถึงตอนนี้ฉันพบว่ามันน่าเชื่อถือและทรงพลัง มันช่วยเบคอนของฉันอย่างน้อยหนึ่งครั้ง

GC ทำงานได้ดีมากใน. NET IMO แต่ก็เหมือนกับภาษาหรือแพลตฟอร์มอื่น ๆ ถ้าคุณเขียนโค้ดไม่ดีสิ่งเลวร้ายก็เกิดขึ้น


3
ใช่ฉันไปกับมันและมันช่วยให้ฉันไปถึงจุดต่ำสุดของการรั่วไหลที่ยุ่งยาก รอยรั่วที่ใหญ่ที่สุดที่ฉันพบนั้นเกิดจากห้องสมุดบุคคลที่สามในรหัสที่ไม่มีการจัดการที่พวกเขาเข้าถึงผ่านทาง interop ฉันประทับใจที่เครื่องมือนี้ตรวจพบรอยรั่วในโค้ดที่ไม่มีการจัดการรวมถึงโค้ดที่มีการจัดการ
Scott Langham

1
ฉันยอมรับคำตอบนี้เป็นคำตอบเพราะสิ่งที่ได้ผลสำหรับฉันในท้ายที่สุด แต่ฉันคิดว่าคำตอบอื่น ๆ ทั้งหมดนั้นมีประโยชน์มาก อย่างไรก็ตามเครื่องมือนี้มักเรียกว่า Mem Profiler ของ SciTech!
Scott Langham

41

สำหรับปัญหาที่ลืมที่จะกำจัดให้ลองวิธีการแก้ปัญหาที่อธิบายไว้ในโพสต์บล็อกนี้ นี่คือสาระสำคัญ:

    public void Dispose ()
    {
        // Dispose logic here ...

        // It's a bad error if someone forgets to call Dispose,
        // so in Debug builds, we put a finalizer in to detect
        // the error. If Dispose is called, we suppress the
        // finalizer.
#if DEBUG
        GC.SuppressFinalize(this);
#endif
    }

#if DEBUG
    ~TimedLock()
    {
        // If this finalizer runs, someone somewhere failed to
        // call Dispose, which means we've failed to leave
        // a monitor!
        System.Diagnostics.Debug.Fail("Undisposed lock");
    }
#endif

ฉันต้องการที่จะโยนข้อยกเว้นแทน DebugFail
Pedro77

17

เราได้ใช้ซอฟต์แวร์Ants Profiler Proโดย Red Gate ในโครงการของเรา มันทำงานได้ดีมากสำหรับแอปพลิเคชันที่ใช้ภาษา. NET ทั้งหมด

เราพบว่า. NET Garbage Collector นั้น "ปลอดภัย" อย่างมากในการล้างวัตถุในหน่วยความจำ (ตามที่ควรจะเป็น) มันจะเก็บวัตถุไว้รอบตัวเพราะเราอาจจะใช้มันในบางครั้งในอนาคต นี่หมายความว่าเราต้องระวังให้มากขึ้นเกี่ยวกับจำนวนของวัตถุที่เราขยายเกินความจำ ในท้ายที่สุดเราแปลงวัตถุข้อมูลทั้งหมดของเราให้เป็น "ขยายตามคำขอ" (ก่อนที่จะขอเขตข้อมูล) เพื่อลดค่าใช้จ่ายของหน่วยความจำและเพิ่มประสิทธิภาพ

แก้ไข: นี่คือคำอธิบายเพิ่มเติมของสิ่งที่ฉันหมายถึงโดย "ขยายความต้องการ" ในรูปแบบวัตถุของเราในฐานข้อมูลของเราเราใช้คุณสมบัติของวัตถุแม่เพื่อเปิดเผยวัตถุลูก (s) ตัวอย่างเช่นถ้าเรามีบันทึกที่อ้างอิง "รายละเอียด" หรือ "ค้นหา" บันทึกอื่น ๆ แบบหนึ่งต่อหนึ่งเราจะจัดโครงสร้างแบบนี้:

class ParentObject
   Private mRelatedObject as New CRelatedObject
   public Readonly property RelatedObject() as CRelatedObject
      get
         mRelatedObject.getWithID(RelatedObjectID)
         return mRelatedObject
      end get
   end property
End class

เราพบว่าระบบดังกล่าวสร้างปัญหาหน่วยความจำจริงและปัญหาประสิทธิภาพเมื่อมีการบันทึกจำนวนมากในหน่วยความจำ ดังนั้นเราจึงเปลี่ยนไปใช้ระบบที่วัตถุมีการขยายเมื่อจำเป็นเท่านั้นและการเรียกฐานข้อมูลจะทำได้เมื่อจำเป็นเท่านั้น:

class ParentObject
   Private mRelatedObject as CRelatedObject
   Public ReadOnly Property RelatedObject() as CRelatedObject
      Get
         If mRelatedObject is Nothing
            mRelatedObject = New CRelatedObject
         End If
         If mRelatedObject.isEmptyObject
            mRelatedObject.getWithID(RelatedObjectID)
         End If
         return mRelatedObject
      end get
   end Property
end class

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


ฉันสองผลิตภัณฑ์นี้ มันเป็นหนึ่งในโปรไฟล์ที่ดีที่สุดที่ฉันใช้
Gord

ฉันพบว่าผู้สร้างโปรไฟล์ค่อนข้างดีสำหรับการดูปัญหาด้านประสิทธิภาพ อย่างไรก็ตามเครื่องมือวิเคราะห์หน่วยความจำค่อนข้างแย่ ฉันพบการรั่วไหลด้วยเครื่องมือนี้ แต่มันเป็นขยะที่ช่วยให้ฉันระบุสาเหตุของการรั่วไหล และมันก็ไม่ได้ช่วยอะไรคุณเลยถ้าการรั่วไหลเกิดขึ้นกับโค้ดที่ไม่มีการจัดการ
Scott Langham

ตกลงเวอร์ชั่นใหม่ 5.1 เป็นสิ่งที่ดีกว่ามาก ดีกว่าที่จะช่วยคุณค้นหาสาเหตุของการรั่วไหล (แม้ว่า - ยังมีปัญหาสองสามข้อที่ ANTS บอกฉันว่าพวกเขาจะแก้ไขในเวอร์ชั่นถัดไป) ยังคงไม่ได้ทำรหัสที่ไม่มีการจัดการ แต่ถ้าคุณไม่ใส่ใจกับรหัสที่ไม่มีการจัดการตอนนี้เป็นเครื่องมือที่ดีทีเดียว
Scott Langham

7

คุณยังคงต้องกังวลเกี่ยวกับหน่วยความจำเมื่อคุณเขียนรหัสที่มีการจัดการเว้นแต่ใบสมัครของคุณไม่สำคัญ ฉันจะแนะนำสองสิ่ง: อันดับแรกอ่านCLR ผ่าน C #เพราะจะช่วยให้คุณเข้าใจการจัดการหน่วยความจำใน. NET ขั้นที่สองเรียนรู้การใช้เครื่องมือเช่นCLRProfiler (Microsoft) สิ่งนี้จะช่วยให้คุณทราบว่าอะไรเป็นสาเหตุของการรั่วไหลของหน่วยความจำของคุณ (เช่นคุณสามารถดูที่การกระจายตัวของกองวัตถุขนาดใหญ่ของคุณ)


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

6

คุณใช้รหัสที่ไม่มีการจัดการหรือไม่? หากคุณไม่ได้ใช้รหัสที่ไม่มีการจัดการตาม Microsoft การรั่วไหลของหน่วยความจำในแง่ที่เป็นไปไม่ได้

หน่วยความจำที่ใช้โดยแอปพลิเคชันอาจไม่ได้รับการเปิดตัวดังนั้นการจัดสรรหน่วยความจำของแอปพลิเคชันอาจเพิ่มขึ้นตลอดอายุของแอปพลิเคชัน

จากวิธีการระบุการรั่วไหลของหน่วยความจำในรันไทม์ภาษาทั่วไปที่ Microsoft.com

หน่วยความจำรั่วสามารถเกิดขึ้นได้ในแอปพลิเคชัน. NET Framework เมื่อคุณใช้รหัสที่ไม่มีการจัดการเป็นส่วนหนึ่งของแอปพลิเคชัน รหัสที่ไม่มีการจัดการนี้สามารถรั่วหน่วยความจำและรันไทม์. NET Framework ไม่สามารถแก้ไขปัญหานั้น

นอกจากนี้โครงการอาจมีหน่วยความจำรั่วเท่านั้น เงื่อนไขนี้สามารถเกิดขึ้นได้หากมีการประกาศวัตถุขนาดใหญ่จำนวนมาก (เช่นวัตถุ DataTable) แล้วเพิ่มลงในคอลเลกชัน (เช่นชุดข้อมูล) ทรัพยากรที่วัตถุเหล่านี้เป็นเจ้าของอาจไม่เคยถูกปล่อยออกมาและทรัพยากรจะคงอยู่ตลอดการทำงานของโปรแกรม สิ่งนี้ดูเหมือนจะรั่ว แต่จริงๆแล้วมันเป็นเพียงอาการของวิธีการจัดสรรหน่วยความจำในโปรแกรม

สำหรับการรับมือกับประเภทของปัญหานี้คุณสามารถใช้IDisposable หากคุณต้องการดูกลยุทธ์ในการจัดการหน่วยความจำฉันขอแนะนำให้ค้นหาIDisposable, XNA, การจัดการหน่วยความจำเนื่องจากผู้พัฒนาเกมจำเป็นต้องมีการรวบรวมขยะที่สามารถคาดเดาได้มากขึ้นและต้องบังคับให้ GC ทำสิ่งนั้น

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


5
ดูblogs.msdn.com/tess/archive/2006/01/23/… . ไม่สำคัญว่าหน่วยความจำรั่วจะเป็น "ดั้งเดิม" หรือไม่ แต่ก็ยังรั่วอยู่
Constantin

2
ฉันเห็นประเด็นของคุณ - แต่การจัดสรรที่ไม่มีประสิทธิภาพและการใช้หน่วยความจำซ้ำโดยโปรแกรมนั้นแตกต่างจากหน่วยความจำรั่ว
Timothy Lee Russell

คำตอบที่ดีขอบคุณสำหรับการจดจำฉันว่าตัวจัดการเหตุการณ์อาจเป็นอันตรายได้
frameworkninja

3
@ Timothy Lee Russel: หากจำนวนหน่วยความจำ (1) ไม่ จำกัด สามารถถูกจัดสรรพร้อมกัน (root) หลังจากไร้ประโยชน์ (2) โดยไม่มีสิ่งใดในระบบที่มีข้อมูลและแรงกระตุ้นที่จำเป็นในการยกเลิกรูทในเวลาที่เหมาะสมนั่นคือหน่วยความจำรั่ว . แม้ว่าหน่วยความจำอาจได้รับการปลดปล่อยสักวันหนึ่งหากมีสิ่งที่ไร้ประโยชน์มากพอที่จะทำให้ระบบหายใจไม่ออกก่อนที่สิ่งนั้นจะเกิดขึ้น (1) มากกว่า O (N), N คือจำนวนของการจัดสรรที่มีประโยชน์ (2) ข้อมูลไร้ประโยชน์หากลบการอ้างอิงถึงมันจะไม่ส่งผลกระทบต่อฟังก์ชันการทำงานของโปรแกรม
supercat

2
@ Timothy Lee Russel: รูปแบบ "memory รั่ว" ปกติเกิดขึ้นเมื่อหน่วยความจำหนึ่งถูกจัดขึ้นในนามของเอนทิตี้อื่นโดยคาดว่าจะได้รับการแจ้งเมื่อไม่ต้องการใช้อีกต่อไป เอนทิตีที่ถือหน่วยความจำนั้นไม่จำเป็นต้องใช้มันจริงๆ แต่ก็ไม่มีทางที่จะตัดสินได้
supercat

5

บล็อกนี้มีคำแนะนำที่ยอดเยี่ยมบางอย่างโดยใช้ windbg และเครื่องมืออื่น ๆ เพื่อติดตามการรั่วไหลของหน่วยความจำทุกประเภท การอ่านที่ยอดเยี่ยมเพื่อพัฒนาทักษะของคุณ


5

ฉันเพิ่งมีหน่วยความจำรั่วในบริการ windows ที่ฉันแก้ไข

ครั้งแรกผมพยายามMemProfiler ฉันพบว่ามันใช้งานยากและไม่เป็นมิตรกับผู้ใช้เลย

จากนั้นฉันใช้JustTraceซึ่งใช้งานง่ายกว่าและให้รายละเอียดเพิ่มเติมเกี่ยวกับวัตถุที่ไม่ได้กำจัดอย่างถูกต้อง

มันทำให้ฉันแก้ปัญหาเรื่องความจำรั่วได้ง่ายจริงๆ


3

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

อย่างไรก็ตาม IMHO จะเป็นการดีกว่าถ้าคุณพิจารณาวิธีการแก้ปัญหาแบบ bespoke - มีเพียงคุณเท่านั้นที่รู้ว่าคุณต้องเก็บวัตถุไว้นานแค่ไหนดังนั้นการออกแบบรหัสการทำความสะอาดที่เหมาะสมสำหรับสถานการณ์ของคุณมักเป็นวิธีที่ดีที่สุด


3

ฉันชอบdotmemoryจาก Jetbrains


คุณอาจเป็นคนเดียว :)
HellBaby

ฉันก็ลองเหมือนกัน ฉันคิดว่านี่เป็นเครื่องมือที่ดี ใช้งานง่ายและให้ข้อมูล รวมเข้ากับ Visual Studio
redeye

ในกรณีของเราเมื่อการแก้ไขปัญหาหน่วยความจำรั่วเครื่องมือ Visual Studio Snapshot ขัดข้อง / ไม่ได้ทำการจับภาพ Dotmemory รักษาความเย็นไว้และจัดการสแนปชอตหลาย ๆ ชุดที่มีขนาด 3+ GB ได้อย่างง่ายดาย
Michael Kargl

3

ปืนใหญ่ - เครื่องมือดีบั๊กสำหรับ Windows

นี่คือชุดเครื่องมือที่น่าทึ่ง คุณสามารถวิเคราะห์ทั้งฮีปที่มีการจัดการและไม่มีการจัดการด้วยและคุณสามารถออฟไลน์ได้ สิ่งนี้มีประโยชน์มากสำหรับการดีบั๊กหนึ่งในแอปพลิเคชัน ASP.NET ของเราที่เก็บการรีไซเคิลเนื่องจากหน่วยความจำมากเกินไป ฉันเพียงต้องสร้างการถ่ายโอนข้อมูลหน่วยความจำแบบเต็มของกระบวนการใช้ชีวิตที่ทำงานอยู่บนเซิร์ฟเวอร์ที่ใช้งานจริงการวิเคราะห์ทั้งหมดทำแบบออฟไลน์ใน WinDbg (ปรากฏว่านักพัฒนาซอฟต์แวร์บางคนใช้พื้นที่เก็บข้อมูลเซสชันในหน่วยความจำมากเกินไป)

"ถ้ามันพัง ... "บล็อกมีบทความที่มีประโยชน์มากในเรื่อง


2

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

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


2

หนึ่งในเครื่องมือที่ดีที่สุดคือการใช้เครื่องมือดีบั๊กสำหรับ Windowsและการถ่ายโอนข้อมูลหน่วยความจำของกระบวนการโดยใช้adplusจากนั้นใช้windbgและปลั๊กอินsosเพื่อวิเคราะห์หน่วยความจำกระบวนการเธรดและสแตกการเรียก

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

จากนั้นวิเคราะห์ออฟไลน์


ใช่มันใช้งานได้ดีโดยเฉพาะอย่างยิ่งสำหรับสิ่งที่สูงขึ้นหรือการวินิจฉัยปัญหาในซอฟต์แวร์ที่วางจำหน่ายซึ่งคุณไม่สามารถแนบดีบักเกอร์ได้อย่างง่ายดาย บล็อกนี้มีเคล็ดลับมากมายเกี่ยวกับการใช้เครื่องมือเหล่านี้ได้เป็นอย่างดี: blogs.msdn.com/tess
Scott Langham

2

หลังจากหนึ่งในการแก้ไขของฉันสำหรับแอปพลิเคชันที่มีการจัดการฉันมีสิ่งเดียวกันเช่นวิธีการตรวจสอบว่าแอปพลิเคชันของฉันจะไม่มีการรั่วไหลของหน่วยความจำเดียวกันหลังจากการเปลี่ยนแปลงครั้งต่อไปดังนั้นฉันจึงเขียนอะไรบางอย่างเช่น แพคเกจ NuGet ObjectReleaseVerification คุณสามารถหาตัวอย่างได้ที่นี่https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sampleและข้อมูลเกี่ยวกับตัวอย่างนี้http://outcoldman.ru/en/blog/show/322


0

จาก Visual Studio 2015 ให้พิจารณาใช้เครื่องมือ วิเคราะห์การใช้หน่วยความจำนอกกรอบเพื่อรวบรวมและวิเคราะห์ข้อมูลการใช้หน่วยความจำ

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


0

หนึ่งในเครื่องมือที่ดีที่สุดที่ฉันใช้ DotMemory.you สามารถใช้เครื่องมือนี้เป็นส่วนเสริมใน VS.after เรียกใช้แอปของคุณคุณสามารถวิเคราะห์ทุกส่วนของหน่วยความจำ (โดย Object, NameSpace, ฯลฯ ) ที่แอปของคุณใช้และถ่ายภาพบางส่วน เปรียบเทียบกับ SnapShots อื่น ๆ DotMemory

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.