เมื่อใดควรใช้การอ้างอิงที่อ่อนแอใน. Net


56

ฉันไม่ได้เจอกับสถานการณ์ที่ฉันต้องการใช้ WeakReference ใน. Net แต่โดยส่วนตัวแล้วความเชื่อที่ได้รับความนิยมดูเหมือนว่าควรจะใช้ในแคช ดรจอน Harrop ให้กรณีที่ดีมากกับการใช้งานของ WeakReferences ในแคชของเขาในคำตอบไปนี้คำถาม

ฉันมักจะได้ยินนักพัฒนา AS3 พูดคุยเกี่ยวกับการใช้การอ้างอิงที่อ่อนแอเพื่อบันทึกในหน่วยความจำ แต่จากการสนทนาที่ฉันมีมันดูเหมือนว่าจะเพิ่มความซับซ้อนโดยไม่จำเป็นต้องบรรลุเป้าหมายที่กำหนดไว้และพฤติกรรมของรันไทม์ค่อนข้างไม่แน่นอน มากเสียจนหลายคนยอมแพ้และจัดการการใช้หน่วยความจำอย่างระมัดระวังมากขึ้น / เพิ่มประสิทธิภาพรหัสของพวกเขาเพื่อให้หน่วยความจำน้อยมาก (หรือทำให้การแลกเปลี่ยนรอบ CPU และหน่วยความจำขนาดเล็กลง)

ดร. จอน Harrop ยังชี้ให้เห็นในคำตอบของเขาว่าการอ้างอิงที่อ่อนแอของ. Net นั้นไม่นุ่มนวลและมีการรวบรวมการอ้างอิงที่อ่อนแอใน gen0 จากการอ้างอิงของMSDNการอ้างอิงที่ไม่รัดกุมจะช่วยให้คุณสามารถสร้างวัตถุใหม่ได้but the state of the object remains unpredictable.!

จากลักษณะเหล่านี้ฉันไม่สามารถนึกถึงสถานการณ์ที่การอ้างอิงที่อ่อนแอจะมีประโยชน์บางทีใครบางคนสามารถทำให้ฉันเข้าใจได้


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

2
@ itsme86 - ฉันไม่ได้กำลังมองหาเคสที่ใช้กระสุนเป็นหลักฐานเพียงอย่างเดียวที่การอ้างอิงที่อ่อนแอนั้นเหมาะสมและเหมาะสม กรณีการใช้แคชตัวอย่างเช่นเนื่องจากการอ้างอิงที่อ่อนแอมีการรวบรวมอย่างกระตือรือร้นมันจะทำให้แคชหายไปมากขึ้นแม้ว่าคุณจะมีหน่วยความจำมากมายที่พร้อมใช้งาน

4
ฉันผิดหวังเล็กน้อยที่นี่ได้รับคะแนนโหวตจำนวนมากเพื่อปิด ฉันไม่รังเกียจที่จะเห็นคำตอบหรือการสนทนาเกี่ยวกับเรื่องนี้ (ใน b4 "Stack Overflow ไม่ใช่ฟอรัม")
ta.speot.is

@thurnurnmonk นั่นคือออฟเซ็ตเป็นการตอบแทนการเพิ่มหน่วยความจำ ในกรอบของวันนี้เป็นที่น่าสงสัยว่าใคร ๆ ก็สามารถเข้าถึงเครื่องมือ WeakReference ได้โดยตรงแม้ในขณะที่ใช้แคชเนื่องจากมีระบบแคชที่ครอบคลุมพร้อมใช้งาน

นี่เป็นตัวอย่างที่ซับซ้อนเกินกว่าที่จะใช้ (สำหรับรูปแบบเหตุการณ์ที่อ่อนแอที่ ta.speot.is อธิบายไว้ด้านล่าง)
Benjol

คำตอบ:


39

ฉันได้พบแอปพลิเคชันเชิงปฏิบัติที่ถูกต้องตามกฎหมายของการอ้างอิงที่อ่อนแอในสถานการณ์จริงสามข้อต่อไปนี้ที่เกิดขึ้นกับฉันเป็นการส่วนตัว:

แอพลิเคชัน 1: ตัวจัดการเหตุการณ์

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

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

แอปพลิเคชัน 2: กราฟที่ไม่แน่นอน

คุณคือ John Carmack คนต่อไป คุณได้คิดค้นการแสดงกราฟบนพื้นผิวของแผนกย่อยแบบลำดับชั้นซึ่งทำให้เกมของ Tim Sweeney ดูเหมือน Nintendo Wii เห็นได้ชัดว่าผมไม่ได้จะบอกคุณว่าวิธีการทำงานDictionary<Vertex, SortedSet<Vertex>>แต่มันศูนย์ทั้งหมดในกราฟที่ไม่แน่นอนนี้ที่เพื่อนบ้านของจุดสุดยอดที่สามารถพบได้ใน โทโพโลยีของกราฟเปลี่ยนแปลงตลอดเวลาที่ผู้เล่นวิ่งไปรอบ ๆ มีเพียงปัญหาเดียว: โครงสร้างข้อมูลของคุณกำลังปล่อยกราฟย่อยที่ไม่สามารถเข้าถึงได้ในขณะที่ทำงานและคุณจำเป็นต้องลบออกหรือคุณจะรั่วหน่วยความจำ โชคดีที่คุณเป็นอัจฉริยะเพื่อให้คุณรู้ว่ามีคลาสอัลกอริทึมที่ออกแบบมาเพื่อค้นหาและรวบรวม subgraphs ที่ไม่สามารถเข้าถึงได้: นักสะสมขยะ! คุณอ่านเอกสารที่ยอดเยี่ยมของ Richard Jones ในเรื่องนี้แต่มันทำให้คุณงุนงงและกังวลเกี่ยวกับกำหนดเวลาใกล้เข้ามาของคุณ คุณทำอะไร?

เพียงแค่แทนที่Dictionaryตารางแฮชที่อ่อนแอคุณสามารถนำ GC ที่มีอยู่กลับมาและให้มันรวบรวมกราฟย่อยที่ไม่สามารถเข้าถึงได้โดยอัตโนมัติสำหรับคุณ! กลับไปพลิกใบโฆษณาเฟอร์รารี

แอพลิเคชัน 3: ตกแต่งต้นไม้

คุณกำลังห้อยลงมาจากเพดานห้อง cyclindrical ที่แป้นพิมพ์ คุณมีเวลา 60 วินาทีในการกลั่นกรองข้อมูลขนาดใหญ่ก่อนที่จะมีคนพบคุณ คุณมาพร้อมกับตัวแยกวิเคราะห์ที่ใช้สตรีมที่สวยงามซึ่งต้องอาศัย GC เพื่อรวบรวมชิ้นส่วนของ AST หลังจากทำการวิเคราะห์แล้ว แต่คุณรู้ว่าคุณต้องการข้อมูลเมตาเพิ่มเติมในแต่ละ AST Nodeและคุณต้องการมันอย่างรวดเร็ว คุณทำอะไร?

คุณสามารถใช้Dictionary<Node, Metadata>เพื่อเชื่อมโยงข้อมูลเมตากับแต่ละโหนด แต่ถ้าคุณไม่ล้างออกการอ้างอิงที่รัดกุมจากพจนานุกรมไปยังโหนด AST เก่าจะทำให้พวกเขามีชีวิตอยู่และหน่วยความจำรั่ว วิธีการแก้ปัญหาคือตารางแฮชแบบอ่อนซึ่งจะเก็บการอ้างอิงที่อ่อนแอไปยังคีย์และขยะเท่านั้นรวบรวมการผูกค่าคีย์เมื่อคีย์ไม่สามารถเข้าถึงได้ จากนั้นเมื่อโหนด AST ไม่สามารถเข้าถึงได้พวกเขาจะถูกเก็บรวบรวมขยะและการเชื่อมโยงค่าคีย์ของพวกเขาจะถูกลบออกจากพจนานุกรมที่ปล่อยให้เมตาดาต้าที่สอดคล้องกันไม่สามารถเข้าถึงได้ จากนั้นสิ่งที่คุณต้องทำหลังจากวนรอบหลักของคุณสิ้นสุดลงคือเลื่อนกลับขึ้นไปในช่องระบายอากาศซึ่งจำได้ว่าจะแทนที่มันเมื่อเจ้าหน้าที่รักษาความปลอดภัยเข้ามา

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


2
การอ้างอิงที่อ่อนแอจะไม่ทำงานสำหรับแอปพลิเคชัน 2 หากกราฟย่อยที่ไม่สามารถเข้าถึงได้มีรอบ นี่เป็นเพราะตารางแฮชที่อ่อนแอมักจะมีการอ้างอิงที่อ่อนแอไปยังคีย์ แต่การอ้างอิงที่แข็งแกร่งกับค่า คุณต้องใช้ตารางแฮชที่รักษาการอ้างอิงที่แข็งแกร่งกับค่าในขณะที่คีย์ยังคงสามารถเข้าถึงได้ -> ดู ephemerons ( ConditionalWeakTableใน. NET)
Daniel

@Daniel GC ไม่น่าจะจัดการกับวัฏจักรที่ไม่สามารถเข้าถึงได้หรือ วิธีนี้จะไม่ถูกเรียกเก็บเมื่อวงจรไม่สามารถเข้าถึงของข้อมูลอ้างอิงที่แข็งแกร่งจะถูกเก็บรวบรวม?
binki

โอ้ฉันคิดว่าฉันเห็น ผมแค่คิดว่าConditionalWeakTableเป็นสิ่งที่ใช้งาน 2 และ 3 จะใช้ในขณะที่บางคนในโพสต์อื่น ๆ Dictionary<WeakReference, T>ที่ใช้จริง ไม่มีความคิดว่าทำไม - คุณมักจะจบลงด้วยค่า null จำนวนมากWeakReferenceด้วยค่าที่ไม่สามารถเข้าถึงได้โดยกุญแจใด ๆ โดยไม่คำนึงว่าคุณจะทำมันอย่างไร Ridik
binki

@binki: "GC ไม่ควรที่จะจัดการกับวัฏจักรที่ไม่สามารถเข้าถึงได้หรือไม่สิ่งนี้จะไม่ถูกรวบรวมเมื่อมีการรวบรวมรอบอ้างอิงที่ไม่สามารถเข้าถึงได้" คุณได้ใส่พจนานุกรมลงในวัตถุที่ไม่ซ้ำที่ไม่สามารถสร้างใหม่ได้ เมื่อวัตถุหลักอย่างใดอย่างหนึ่งของคุณไม่สามารถเข้าถึงได้มันอาจเป็นการรวบรวมขยะ แต่ค่าที่เกี่ยวข้องในพจนานุกรมจะไม่แม้แต่จะคิดว่ามันไม่สามารถเข้าถึงได้ในทางทฤษฎีเพราะพจนานุกรมสามัญจะมีการอ้างอิงที่แข็งแกร่งทำให้มีชีวิตอยู่ ดังนั้นคุณใช้พจนานุกรมที่อ่อนแอ
Jon Harrop

@Daniel: "การอ้างอิงที่อ่อนแอจะไม่ทำงานสำหรับแอปพลิเคชัน 2 หากกราฟย่อยที่ไม่สามารถเข้าถึงได้มีวัฏจักรอยู่เนื่องจากตารางแฮชที่อ่อนแอมักจะมีการอ้างอิงที่ไม่ดีกับคีย์ แต่การอ้างอิงที่แข็งแกร่งกับค่าต่างๆ รักษาการอ้างอิงที่แข็งแกร่งไปยังค่าในขณะที่คีย์ยังคงสามารถเข้าถึงได้ " ใช่. คุณน่าจะดีกว่าการเข้ารหัสกราฟด้วยขอบโดยตรงเป็นพอยน์เตอร์เพื่อให้ GC จะรวบรวมมันเอง
Jon Harrop

19

จากลักษณะเหล่านี้ฉันไม่สามารถนึกถึงสถานการณ์ที่การอ้างอิงที่อ่อนแอจะมีประโยชน์บางทีใครบางคนสามารถทำให้ฉันเข้าใจได้

เอกสาร Microsoft รูปแบบการจัดกิจกรรมที่อ่อนแอ

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

...

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


URL นั้นจะเลือกเวอร์ชันล่าสุดของ. NET อัตโนมัติ (ปัจจุบันคือ 4.5) ซึ่ง 'ไม่สามารถใช้หัวข้อนี้ได้อีกต่อไป' การเลือก. NET 4.0 จะใช้งานได้แทน ( msdn.microsoft.com/en-us/library/aa970850(v=vs.100).aspx )
maxp

13

ให้ฉันนำสิ่งนี้ออกก่อนแล้วค่อยกลับมาหามัน:

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

ดังนั้นเริ่มจากจุดเริ่มต้น:

- แผนการล่วงหน้าสำหรับความผิดที่ไม่ได้ตั้งใจ แต่ฉันจะกลับไปที่ระดับ "ดิ๊กและเจน" สักครู่หนึ่งเพราะไม่มีใครสามารถบอกผู้ชมได้

ดังนั้นเมื่อคุณมีวัตถุX- ลองระบุเป็นตัวอย่างclass Foo- มันไม่สามารถอยู่ได้ด้วยตัวมันเอง (ส่วนใหญ่เป็นจริง); ในทำนองเดียวกับที่ว่า "ไม่มีมนุษย์คนใดเป็นเกาะ" มีเพียงไม่กี่วิธีที่วัตถุสามารถเลื่อนตำแหน่งเป็น Islandhood ได้แม้ว่าจะเรียกว่าเป็นราก GC ใน CLR ก็ตาม การเป็น GC Root หรือมีการเชื่อมต่อ / อ้างอิงไปยัง GC root เป็นสิ่งที่กำหนดว่าจะได้Foo x = new Foo()รับขยะหรือไม่

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

ณ จุดนี้เรามาดูตัวอย่างที่น่ากลัว:

ก่อนอื่นเราFoo:

public class Foo 
{
    private static volatile int _ref = 0;
    public event EventHandler FooEvent;
    public Foo()
    {
        _ref++;
        Console.WriteLine("I am #{0}", _ref);
    }
    ~Foo()
    {
        Console.WriteLine("#{0} dying!", _ref--);
    }
}

ค่อนข้างง่าย - ไม่ปลอดภัยนักดังนั้นอย่าลองทำเช่นนั้น แต่รักษา "การอ้างอิงนับ" ของอินสแตนซ์ที่ใช้งานและการลดลงอย่างคร่าวๆเมื่อสรุปเสร็จแล้ว

ตอนนี้เรามาดูFooConsumer:

public class NastySingleton
{
    // Static member status is one way to "get promoted" to a GC root...
    private static NastySingleton _instance = new NastySingleton();
    public static NastySingleton Instance { get { return _instance;} }

    // testing out "Hard references"
    private Dictionary<Foo, int> _counter = new Dictionary<Foo,int>();
    // testing out "Weak references"
    private Dictionary<WeakReference, int> _weakCounter = new Dictionary<WeakReference,int>();

    // Creates a strong link to Foo instance
    public void ListenToThisFoo(Foo foo)
    {
        _counter[foo] = 0;
        foo.FooEvent += (o, e) => _counter[foo]++;
    }

    // Creates a weak link to Foo instance
    public void ListenToThisFooWeakly(Foo foo)
    {
        WeakReference fooRef = new WeakReference(foo);
        _weakCounter[fooRef] = 0;
        foo.FooEvent += (o, e) => _weakCounter[fooRef]++;
    }

    private void HandleEvent(object sender, EventArgs args, Foo originalfoo)
    {
        Console.WriteLine("Derp");
    }
}

ดังนั้นเราจึงมีวัตถุที่เป็นรากของ GC อยู่แล้ว (ดี ... ที่เฉพาะเจาะจงมันจะถูกรูทผ่านทางสายตรงไปยังโดเมนแอปที่รันแอปพลิเคชันนี้ แต่เป็นอีกหัวข้อหนึ่ง) ที่มีสองวิธี ของการล็อคไปยังFooอินสแตนซ์ - ลองทดสอบดู:

// Our foo
var f = new Foo();

// Create a "hard reference"
NastySingleton.Instance.ListenToThisFoo(f);

// Ok, we're done with this foo
f = null;

// Force collection of all orphaned objects
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

ตอนนี้จากข้างต้นคุณคาดหวังหรือไม่ว่าวัตถุที่เคยถูกอ้างถึงโดยครั้งนี้fจะเรียกว่า "รวบได้"?

ไม่เนื่องจากมีวัตถุอื่นที่มีการอ้างอิงอยู่ในขณะนี้ - DictionaryในSingletonอินสแตนซ์แบบคงที่

ตกลงลองใช้วิธีการที่อ่อนแอ:

f = new Foo();
NastySingleton.Instance.ListenToThisFooWeakly(f);

// Ok, we're done with this foo
f = null;

// Force collection of all orphaned objects
// This should collect # 2 - you'll see a "#2 dying"
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

ตอนนี้เมื่อเราตีการอ้างอิงของเรากับFoo- นั่นคือ - ครั้งเดียว - fไม่มีการอ้างอิง "ยาก" อีกต่อไปกับวัตถุดังนั้นจึงสามารถรวบรวมได้ - การWeakReferenceสร้างโดยผู้ฟังที่อ่อนแอจะไม่ป้องกันสิ่งนั้น

กรณีการใช้งานที่ดี:

  • ตัวจัดการเหตุการณ์ (แม้ว่าจะอ่านก่อนนี้: เหตุการณ์ที่อ่อนแอใน C # )

  • คุณมีสถานการณ์ที่คุณจะทำให้เกิด "การอ้างอิงซ้ำ" (เช่นวัตถุ A หมายถึงวัตถุ B ซึ่งหมายถึงวัตถุ A หรือที่เรียกว่า "หน่วยความจำรั่ว") (แก้ไข: derp แน่นอนว่านี่ไม่ใช่ ไม่เป็นความจริง)

  • คุณต้องการที่จะ "ออกอากาศ" บางสิ่งบางอย่างกับชุดของวัตถุ แต่คุณไม่ต้องการที่จะเป็นสิ่งที่ทำให้พวกเขามีชีวิตอยู่ List<WeakReference>สามารถรักษาได้อย่างง่ายดายและแม้กระทั่งการตัดแต่งโดยการเอาที่ref.Target == null


1
เกี่ยวกับกรณีการใช้งานครั้งที่สองของคุณตัวรวบรวมขยะจัดการการอ้างอิงแบบวงกลมได้ดี "วัตถุ A หมายถึงวัตถุ B ซึ่งหมายถึงวัตถุ A" ไม่ได้เป็นหน่วยความจำรั่วแน่นอน
Joe Daley

@JoeDaley ฉันเห็นด้วย .NET GC ใช้อัลกอริธึมการทำเครื่องหมายและการกวาดซึ่ง (ฉันเชื่อว่าฉันจำได้ถูกต้อง) ทำเครื่องหมายวัตถุทั้งหมดสำหรับการรวบรวมแล้วตามการอ้างอิงจาก "ราก" (การอ้างอิงของวัตถุบนกองซ้อนวัตถุคงที่) วัตถุที่ไม่ทำเครื่องหมายสำหรับการรวบรวม . หากมีการอ้างอิงแบบวงกลม แต่ไม่มีวัตถุใดที่สามารถเข้าถึงได้จากรูทวัตถุนั้นจะไม่ถูกทำเครื่องหมายสำหรับการรวบรวมและจึงมีคุณสมบัติเหมาะสมสำหรับการรวบรวม
ta.speot.is

1
@JoeDaley - คุณทั้งคู่ถูกต้องแล้ว - กำลังไปที่นั่นจนจบ ... ฉันจะแก้ไขมัน
JerKimball

4

ป้อนคำอธิบายรูปภาพที่นี่

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

พิจารณาสิ่งที่เกิดขึ้นหากผู้ใช้ร้องขอให้ลบทรัพยากรแอปพลิเคชันด้านบนThing2ล้มเหลวในการจัดการกับเหตุการณ์ดังกล่าวภายใต้:

  1. ตัวชี้
  2. การอ้างอิงที่แข็งแกร่ง
  3. ข้อมูลอ้างอิงที่อ่อนแอ

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


1

ตัวอย่างที่เป็นตัวอย่างของการอ้างอิงที่อ่อนแอซึ่งใช้เพื่อผลที่ดีคือConditionalWeakTableซึ่ง DLR ใช้ (ในที่อื่น ๆ ) เพื่อแนบ "สมาชิก" เพิ่มเติมกับวัตถุ

คุณไม่ต้องการให้โต๊ะรักษาวัตถุให้มีชีวิตอยู่ แนวคิดนี้ไม่สามารถทำงานได้หากไม่มีการอ้างอิงที่อ่อนแอ

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


ฉันได้ค้นพบจริงแล้วว่าถึงแม้ว่าตารางจะใช้แนวคิดของการอ้างอิงที่อ่อนแอ แต่การใช้งานจริงไม่เกี่ยวข้องกับWeakReferenceประเภทเนื่องจากสถานการณ์มีความซับซ้อนมากขึ้น มันใช้ฟังก์ชั่นต่าง ๆ ที่เปิดเผยโดย CLR
GregRos

-2

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

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

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


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

1
แต่สำหรับจุดที่สองของคุณเกี่ยวกับวัตถุขนาดใหญ่เอกสาร MSDN ระบุว่าในขณะที่การอ้างอิงที่มีความยาวน้อยช่วยให้คุณสามารถสร้างวัตถุได้อีกครั้งสถานะของมันยังคงไม่แน่นอน หากคุณกำลังจะสร้างมันขึ้นมาใหม่ทุกครั้งทำไมต้องใช้การอ้างอิงที่อ่อนแอเมื่อคุณสามารถเรียกฟังก์ชั่น / วิธีการเพื่อสร้างมันตามต้องการและส่งคืนอินสแตนซ์ชั่วคราว

มีสถานการณ์หนึ่งที่การแคชมีประโยชน์: หากมีการสร้างวัตถุที่ไม่เปลี่ยนรูปบ่อยครั้งซึ่งหลาย ๆ สิ่งจะเกิดขึ้นเหมือนกัน (เช่นการอ่านหลายบรรทัดจากไฟล์ที่คาดว่าจะมีจำนวนซ้ำกัน) แต่ละสตริงจะถูกสร้างเป็นวัตถุใหม่ แต่ถ้าบรรทัดตรงกับอีกบรรทัดที่มีการอ้างอิงอยู่แล้วประสิทธิภาพของหน่วยความจำอาจได้รับการปรับปรุงหากมีการยกเลิกอินสแตนซ์ใหม่และการอ้างอิงไปยังอินสแตนซ์ที่มีอยู่ก่อนจะถูกแทนที่ โปรดทราบว่าการทดแทนนี้มีประโยชน์เพราะมีการอ้างอิงอื่นอยู่ หากไม่ใช่รหัสควรเก็บรหัสใหม่
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.