เมื่อใดที่คุณจะใช้ WeakHashMap หรือ WeakReference


163

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



ที่คล้ายกัน: การใช้ WeakHashMap?
Basil Bourque

คำตอบ:


96

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

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

การทำความเข้าใจข้อมูลอ้างอิงที่อ่อนแออีธานนิโคลัส


43
SoftReferences จะไม่ดีกว่าในกรณีนี้เช่นการอ้างอิงที่ถูกรวบรวมเมื่อหน่วยความจำเริ่มหมด
JesperE

ฉันสับสนเล็กน้อย ... สมมติว่าฉันมี SWT Image cache ต้องลบภาพ SWT ด้วยวิธีการกำจัด () เพื่อปล่อยทรัพยากร SO หากฉันใช้ WeakHashMap เพื่อเก็บพวกมันแสดงว่า GC จะกำจัดวัตถุอย่างแน่นอนหรือไม่
marcolopes

2
@marcolopes GC จะใช้ finalizer กับวัตถุอื่น ดูเหมือนว่า SWT จะไม่ชอบเมื่อคุณทำเช่นนั้นดังนั้นฉันไม่คิดว่าคุณจะสามารถจัดการทรัพยากรระบบปฏิบัติการด้วย a WeakHashMapได้
Jacob Krall

@marcolopes (ฉันจะสมมติว่า GC ของคุณรับประกันการโทรให้สรุปก่อนที่จะเรียกคืนหน่วยความจำ) หากการกำจัดเสร็จสิ้นในแคช finalizer แคชทั้งหมดจะดี หากการกำจัดเป็นสิ่งที่คุณต้องโทรด้วยตนเองจากนั้น 1) ขยายคลาสและวางจำหน่ายใน finalizer หรือ 2) ใช้ phantomreference เพื่อติดตามและเรียกใช้การกำจัดตามลำดับ ตัวเลือกที่ 2 นั้นดีกว่า (หลีกเลี่ยงข้อผิดพลาดในการฟื้นคืนชีพและให้ความสามารถในการเรียกใช้จัดการกับเธรดอื่น) แต่ตัวเลือกที่ 1 นั้นง่ายกว่าที่จะใช้โดยไม่มี helperclasses
Pacerier


55

WeakReference กับ SoftReference

ความแตกต่างหนึ่งที่จะมีความชัดเจนเกี่ยวกับความแตกต่างระหว่างที่และWeakReferenceSoftReference

โดยทั่วไป a WeakReferenceจะเป็นGC-dโดยJVMอย่างกระตือรือร้นเมื่อวัตถุที่อ้างอิงไม่มีการอ้างอิงอย่างหนัก SoftReferenced วัตถุบนมืออื่น ๆ ที่จะมีแนวโน้มที่จะถูกทิ้งไว้เกี่ยวกับการเก็บขยะจนจริงๆต้องเรียกคืนหน่วยความจำ

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


4
"แคชที่เก็บค่าไว้ภายใน WeakReferences นั้นค่อนข้างไร้ประโยชน์" ฉันไม่เห็นด้วยเลย
Thomas Eding

5
@trinithis - เอ่อฉันไม่รู้จะพูดยังไงดี ทำไมแคชที่มีค่าหายไปเมื่อคุณไม่ได้อ้างอิงถึงสิ่งที่มีประโยชน์จริง
oxbow_lakes

4
สำหรับสิ่งที่ต้องการบันทึกช่วยจำแคชที่เก็บค่าแคชไว้อย่างหลวม ๆ อาจมีประโยชน์
Thomas Eding

5
@ThomasEding ฉันยังไม่เข้าใจ ครั้งเดียวที่แคชดูเหมือนว่ามีประโยชน์คือเมื่อไม่มีการอ้างอิงอื่นไปถึง ... หากคุณมีการอ้างอิงถึงสิ่งใดที่คุณต้องการแคช
Cruncher

2
@ThomasEding, Softref บอกสภาพแวดล้อม "เก็บสิ่งนี้ไว้จนกว่าคุณจะไม่มีหน่วยความจำ" Weakref บอกสภาพแวดล้อม "เก็บสิ่งนี้ไว้จนกว่าจะรัน GC" ไม่มีกรณีการใช้งานที่ชัดเจนสำหรับจุดอ่อนถ้าคุณกำลังดีบัก / ทำโปรไฟล์ GC เอง หากคุณต้องการแคชที่ไวต่อหน่วยความจำให้ใช้ softref หากคุณไม่ต้องการแคชอย่าทำแคช! จุดอ่อนเข้ามาที่ไหน
Pacerier

30

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

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

ผมต้องทำเช่นนี้เพื่อเพิ่มข้อมูลบางอย่างเพื่อjava.awt.Componentที่จะได้รอบการเปลี่ยนแปลงในระหว่าง JRE 1.4.2 และ 1.5 ฉันจะได้รับการแก้ไขได้โดย subclassing ส่วนประกอบผมเป็น int สนใจ (ทุกJButton, JFrame, JPanel.... ) แต่ตอนนี้มาก ง่ายขึ้นด้วยรหัสน้อย


1
WeakHashMap รู้ได้อย่างไรว่า "โปรแกรมเหล่านี้ไม่ได้ถูกใช้ในส่วนที่เหลือของคุณ" อีกต่อไป
Vinoth Kumar CM

2
Hasmap ที่อ่อนแอใช้การอ้างอิงที่ไม่รัดกุมสำหรับคีย์ของมัน เมื่อวัตถุถูกอ้างอิงผ่านการอ้างอิงที่อ่อนแอตัวรวบรวมขยะจะ 'แจ้งเตือน' เจ้าของการอ้างอิงที่อ่อนแอ (ในกรณีนี้คือ WeaHashMap) ฉันจะอ่านเพิ่มเติมเกี่ยวกับ WeakReferences และ ReferenceQueues ใน javadocs เพื่อทำความเข้าใจว่าวัตถุเหล่านี้มีปฏิกิริยาอย่างไรกับตัวเก็บขยะ
ลูกา

1
ขอบคุณลุคคุณสามารถให้รหัสง่าย ๆ สำหรับคุณอธิบายคำอธิบายได้หรือไม่?
น้ำต้ม

ดังนั้นการอ้างอิงที่ไม่เหมาะสมจึงมีความหมายเฉพาะใน Javaเนื่องจาก API แปลก ๆ ของ Java ที่บางคลาสไม่สามารถขยายได้
Pacerier

22

อีกกรณีที่มีประโยชน์สำหรับการWeakHashMapและWeakReferenceเป็นการดำเนินการรีจิสทรีฟัง

เมื่อคุณสร้างบางสิ่งที่ต้องการฟังเหตุการณ์บางอย่างโดยปกติคุณจะลงทะเบียนผู้ฟังเช่น

manager.registerListener(myListenerImpl);

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

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

ในกรณีที่ไม่WeakHashMapเข้ามาในภาพหรือไม่

รีจิสตรีผู้ฟังที่ต้องการจัดเก็บ listeners ที่รีจิสเตอร์เนื่องจากWeakReferenceต้องการคอลเล็กชันเพื่อเก็บการอ้างอิงเหล่านี้ ไม่มีWeakHashSetการนำไปใช้ในไลบรารี Java มาตรฐานเพียงอย่างเดียวWeakHashMapแต่เราสามารถใช้อันหลังเพื่อ "ใช้" ฟังก์ชั่นของอันแรก:

Set<ListenerType> listenerSet =
    Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());

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


10
ปัญหาเกี่ยวกับการใช้ weakHashSets สำหรับ listener list คืออินสแตนซ์ listener ที่ไม่ระบุชื่อที่สร้างใน register () จะหายไปได้ง่ายซึ่งผู้ใช้จะคาดไม่ถึง มันปลอดภัยกว่าที่จะเก็บข้อมูลอ้างอิงที่แข็งแกร่งให้กับผู้ฟังจากผู้จัดการแทนและพึ่งพาผู้เรียกเพื่อทำสิ่งที่ถูกต้องแทน
Gunanaresh

สำหรับการใช้งานฟังผู้ลงทะเบียน: ผู้ฟังที่ลงทะเบียนทั้งหมดจะถูกรวบรวม / ทำลายในการเตะ GC ครั้งต่อไปหรือไม่ ตัวอย่างเช่นในขณะที่ใช้วิธีการ onSomethingHappened () ของผู้ฟังทุกคนจะเกิดอะไรขึ้นถ้า GC เตะ?
blackkara

@icza ฉันไม่อยากจะเชื่อว่าผู้คนยังคงเร่ขายตำนานนี้อยู่ นี่เป็นคำตอบที่ผิดอย่างสมบูรณ์ คุณอาจพูดอีกกรณีที่มีประโยชน์สำหรับWeakHashMapเมื่อใดก็ตามที่คุณต้องการHashMapวัตถุ ดังนั้นคุณจะไม่ต้องทำhashmap.remove ด้วยตนเองเลยเพราะรายการจะถูกลบออกโดยอัตโนมัติเมื่อ obj อยู่นอกขอบเขต! มายากลอย่างแท้จริง! ดังกล่าวสับมายากลที่น่าเกลียดเป็นfacepalm สมบูรณ์
Pacerier

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

1
@Pierier ฉันก็ไม่เข้าใจคำคัดค้านของคุณ ในสถานการณ์แบบ Publish-Register หรือ Event Bus ชุดของข้อมูลอ้างอิงอ่อน ๆ นั้นสมเหตุสมผลดีสำหรับฉัน วัตถุที่สมัครสมาชิกได้รับอนุญาตให้ออกนอกขอบเขตและมุ่งหน้าไปยังการเก็บขยะโดยไม่จำเป็นต้องยกเลิกการสมัครอย่างเป็นทางการ กระบวนการของการยกเลิกการสมัครนั้นมีความซับซ้อนเป็นพิเศษหากวัตถุของบุคคลที่สามมีหน้าที่รับผิดชอบในการสมัครสมาชิกครั้งแรก ชุดของWeakReferenceช่วยลดความยุ่งยากอย่างมากฐานรหัสและหลีกเลี่ยงข้อบกพร่องที่ไม่จำเป็นที่เกี่ยวข้องกับการล้มเหลวในการยกเลิกการสมัคร ข้อเสียคืออะไร
Basil Bourque

5

บล็อกโพสต์นี้แสดงให้เห็นถึงการใช้หลักสูตรทั้ง: Java: ตรงกันเกี่ยวกับประชาชน การใช้งานมีลักษณะดังนี้:

private static IdMutexProvider MUTEX_PROVIDER = new IdMutexProvider();

public void performTask(String resourceId) {
    IdMutexProvider.Mutex mutext = MUTEX_PROVIDER.getMutex(resourceId);
    synchronized (mutext) {
        // look up the resource and do something with it
    }
}

IdMutextProvider จัดเตรียมวัตถุที่เป็น id เพื่อซิงโครไนซ์ ข้อกำหนดคือ:

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

นี่คือความสำเร็จโดยใช้แผนที่จัดเก็บข้อมูลภายในประเภท:

WeakHashMap<Mutex, WeakReference<Mutex>>

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


3

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

ตอนนี้ถ้าใครบางคนสามารถอธิบายการอ้างอิงผีกับฉันฉันจะมีความสุข ...


2
การใช้งานครั้งเดียว: PhantomReferences ช่วยให้คุณกำหนดได้อย่างแม่นยำเมื่อวัตถุถูกลบออกจากหน่วยความจำ พวกเขาเป็นวิธีเดียวที่จะตัดสินได้ ( weblogs.java.net/blog/enicholas/archive/2006/05/… )
Jacob Krall

จริงๆแล้วมันจะไม่ถูกลบออกจนกว่าคุณจะล้างมันอย่างชัดเจน "ซึ่งแตกต่างจากการอ้างอิงแบบอ่อนและอ่อนการอ้างอิงแบบแฝงจะไม่ถูกล้างโดยตัวเก็บขยะโดยอัตโนมัติเมื่อมีการจัดคิววัตถุที่เข้าถึงได้ผ่านการอ้างอิงแบบผีจะยังคงอยู่จนกว่าจะมีการล้างข้อมูลอ้างอิงทั้งหมด
jontro

@ jontro, แต่มันได้รับการสรุปแล้ว, สมาชิกทั้งหมดหายไป อย่างมีประสิทธิภาพมันเป็น obj ว่างเปล่า ดู stackoverflow.com/q/7048767/632951
Pacerier

3

ตามที่ระบุไว้ข้างต้นการอ้างอิงที่อ่อนแอจะถูกเก็บไว้ตราบเท่าที่มีการอ้างอิงที่แข็งแกร่งอยู่

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

ในขณะที่เล่นกับผู้ฟังตามที่อธิบายไว้ข้างต้นเราตระหนักได้อย่างรวดเร็วว่าวัตถุได้รับการรวบรวม "ทันที" จากมุมมองของผู้ใช้


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

คำตอบนี้ผิดอย่างสมบูรณ์ การทำอย่างละเอียด: stackoverflow.com/questions/154724/…
Pacerier

@Pacerier - สำหรับWeakReferencesความคิดเห็นของคุณผิดอย่างสิ้นเชิง!

2

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


1
นี่คือความนุ่มนวลไม่ใช่ความอ่อนแอ ดูstackoverflow.com/a/155492/632951
Pacerier


-1

คุณสามารถใช้ weakhashmap เพื่อใช้การแคชที่ไม่มีทรัพยากรสำหรับการสร้างวัตถุที่กว้างขวาง

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


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