เหตุใดคุณจึงต้องทำให้การใช้งานเสร็จสิ้น ()


371

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

ฉันไม่ได้เรียนใน CS แต่ฉันได้เขียนโปรแกรมใน Java อย่างมืออาชีพมาเกือบทศวรรษแล้วและฉันไม่เคยเห็นใครใช้finalize()ระบบการผลิตเลย นี่ยังไม่ได้หมายความว่ามันไม่มีประโยชน์หรือคนที่ฉันทำงานด้วยถูกต้องแล้ว

ดังนั้นคำถามของฉันคือกรณีใดบ้างที่มีการใช้งานfinalize()ที่ไม่สามารถจัดการได้อย่างน่าเชื่อถือมากขึ้นผ่านกระบวนการหรือไวยากรณ์อื่นภายในภาษา

โปรดระบุสถานการณ์ที่เฉพาะเจาะจงหรือประสบการณ์ของคุณเพียงแค่ทำซ้ำตำราเรียนภาษา Java หรือการใช้งานที่ตั้งใจไว้ไม่เพียงพอเนื่องจากไม่ใช่เจตนาของคำถามนี้


HI finalize () วิธีการอธิบายได้ดีมากที่นี่ howtodoinjava.com/2012/10/31/…
Sameer Kazi

2
finalize()การประยุกต์ใช้มากที่สุดและรหัสห้องสมุดจะไม่ใช้ อย่างไรก็ตามรหัสไลบรารีแพลตฟอร์มเช่นSocketInputStreamซึ่งจัดการทรัพยากรดั้งเดิมในนามของผู้เรียกใช้พยายามลดความเสี่ยงของการรั่วไหลของทรัพยากร (หรือใช้กลไกที่เทียบเท่าเช่นPhantomReferenceที่เพิ่มเข้ามาในภายหลัง) ดังนั้นระบบนิเวศต้องการพวกเขา แม้ว่านักพัฒนา 99.9999% จะไม่เคยเขียน
Brian Goetz

คำตอบ:


231

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

นำfinalize()ไปใช้เพื่อทำการclose()ประมวลผลหากคุณตรวจพบว่ายังไม่ได้ดำเนินการ อาจมีบางอย่างที่ถูกทิ้งไปstderrเพื่อชี้ให้เห็นว่าคุณกำลังล้างข้อมูลหลังจากผู้โทรที่บั๊กกี้

ให้ความปลอดภัยเป็นพิเศษในสถานการณ์พิเศษ / บั๊กกี้ ไม่ใช่ผู้โทรทุกคนที่จะทำtry {} finally {}สิ่งที่ถูกต้องทุกครั้ง โชคร้าย แต่จริงในสภาพแวดล้อมส่วนใหญ่

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

ฉันเห็นว่าเป็นของ Java 9, Object.finalize()เลิกใช้แล้ว! พวกเขาชี้ให้เราjava.lang.ref.Cleanerและjava.lang.ref.PhantomReferenceเป็นทางเลือก


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

17
การสรุปไม่รับประกันว่าจะถูกเรียกใช้ดังนั้นอย่าวางใจในการปล่อยทรัพยากรออกมา
สะบัด

19
skaffman - ฉันไม่เชื่อ (นอกเหนือจากการติดตั้ง JVM แบบบั๊กกี้) จาก Object.finalize () javadoc: หากมีการโยนข้อยกเว้นที่ไม่ได้ตรวจสอบโดยวิธีการสิ้นสุดข้อยกเว้นจะถูกละเว้นและการสรุปของวัตถุนั้นจะสิ้นสุดลง
John M

4
ข้อเสนอของคุณสามารถซ่อนข้อบกพร่องของผู้คน (ไม่ได้โทรเข้ามาใกล้) เป็นเวลานาน ฉันจะไม่แนะนำให้ทำเช่นนั้น
kohlerm

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

174

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

การทำสิ่งที่สำคัญใน finalizers (โดยพื้นฐานแล้วยกเว้นการบันทึก) ก็ทำได้ดีในสามสถานการณ์

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

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

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

ข้อจำกัดความรับผิดชอบ: ฉันเคยทำงานเกี่ยวกับการดำเนินการ JVM มาก่อน ฉันเกลียดขั้นตอนสุดท้าย


76
การถอนรากถอนโคนสำหรับกระบวนการยุติธรรมที่รุนแรง
การรับรู้

32
สิ่งนี้ไม่ตอบคำถาม มันแค่บอกว่าข้อเสียทั้งหมดfinalize()โดยไม่ให้เหตุผลใด ๆ ที่ใคร ๆ ก็อยากจะใช้มัน
หอยทากวิศวกรรม

1
@supercat: การอ้างอิงแฝง (สำหรับเรื่องนั้นการอ้างอิงทั้งหมด) ถูกนำมาใช้ใน Java 2
Steve Jessop

1
@supercat: ขอโทษใช่โดย "อ้างอิง" ฉันหมายถึงjava.lang.ref.Referenceและ subclasses Java 1 มีตัวแปร :-) และใช่มันมี finalizers ด้วย
Steve Jessop

2
@SteveJessop: ถ้าตัวสร้างคลาสฐานขอให้หน่วยงานอื่น ๆ เปลี่ยนสถานะของพวกเขาในนามของวัตถุภายใต้การก่อสร้าง (รูปแบบที่พบบ่อยมาก) แต่ initializer ภาคสนามที่ได้มาโยนข้อยกเว้นวัตถุที่สร้างขึ้นบางส่วนควรล้างทันที หลังจากตัวเอง (เช่นแจ้งหน่วยงานภายนอกที่ไม่จำเป็นต้องใช้บริการของพวกเขาอีกต่อไป) แต่ Java หรือ. NET ไม่ได้เสนอรูปแบบการทำความสะอาดจากระยะไกลที่สามารถเกิดขึ้นได้ แน่นอนพวกเขาดูเหมือนจะออกไปเพื่อทำให้มันยากสำหรับการอ้างอิงถึงสิ่งที่ต้องมีการล้างข้อมูลเพื่อเข้าถึงรหัสการล้างข้อมูล
supercat

58

กฎง่ายๆ: ห้ามใช้ Finalizers

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

จากบทความโดย Brian Goetz:

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


46

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


35

ฉันทำ Java อย่างมืออาชีพมาตั้งแต่ปี 1998 และฉันไม่เคยใช้finalize()เลย ไม่ใช่ครั้งเดียว


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

2
@ การเรียกขั้นสุดท้ายของ InfantPro'Aravind ไม่ได้ลบวัตถุ นี่เป็นวิธีการที่คุณนำไปใช้ซึ่ง JVM สามารถเลือกที่จะโทรถ้าและเวลาที่ขยะรวบรวมวัตถุ
Paul Tomblin

@ พอลทอมบลินโอ้ขอบคุณสำหรับรายละเอียดที่ มีข้อเสนอแนะในการรีเฟรชหน้าใน ADF หรือไม่
InfantPro'Aravind '

@ InfantPro'Aravind 'ไม่คิดไม่เคยใช้ ADF
พอลทอมบลิน

28

คำตอบที่ได้รับการยอมรับเป็นสิ่งที่ดีฉันแค่อยากจะเพิ่มว่าตอนนี้มีวิธีที่จะมีฟังก์ชั่นของการจบโดยไม่ต้องใช้มันเลย

ดูคลาส "อ้างอิง" การอ้างอิงที่อ่อนแอ, การอ้างอิงแบบผี & การอ้างอิงแบบอ่อน

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

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

public void finalize() {
  ref1 = null;
  ref2 = null;
  othercrap = null;
}

มันเป็นสัญญาณว่ามีคนไม่รู้ว่ากำลังทำอะไรอยู่ "ทำความสะอาด" เช่นนี้แทบไม่จำเป็นเลย เมื่อชั้นเรียนเป็น GC'd สิ่งนี้จะทำโดยอัตโนมัติ

หากคุณพบรหัสเช่นนั้นในขั้นตอนสุดท้ายก็รับประกันได้ว่าผู้ที่เขียนมันสับสน

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

ไม่ควรใช้ทำความสะอาด "เร็วกว่า"


3
การอ้างอิงจาก Phantom เป็นวิธีที่ดีกว่าในการติดตามว่าวัตถุใดที่ถูกปลดปล่อยให้เป็นอิสระฉันคิดว่า
Bill Michell

2
upvoting สำหรับการให้คำอธิบายที่ดีที่สุดของ "การอ้างอิงที่อ่อนแอ" ฉันเคยได้ยินมา
sscarduzio

ฉันขอโทษที่ต้องใช้เวลาหลายปีกว่าจะได้อ่านสิ่งนี้ แต่จริงๆแล้วมันเป็นคำตอบที่ดีมาก
Spencer Kormos

27

ฉันไม่แน่ใจว่าคุณสามารถทำสิ่งนี้ได้ แต่ ...

itsadok@laptop ~/jdk1.6.0_02/src/
$ find . -name "*.java" | xargs grep "void finalize()" | wc -l
41

ดังนั้นฉันเดาว่าอาทิตย์พบว่ามีบางกรณีที่ควรใช้


12
ฉันเดาว่ามันเป็นคำถามของ "นักพัฒนา Sun จะถูกต้องเสมอหรือไม่"
CodeReaper

13
แต่มีการใช้งานจำนวนเท่าไรในการใช้งานหรือทดสอบการสนับสนุนfinalizeมากกว่าการใช้งานจริง
หอยทากวิศวกรรม

ฉันใช้ Windows คุณจะทำสิ่งเดียวกันใน Windows ได้อย่างไร
gparyani

2
@damryfbfnetsi: ลองติดตั้งแอ๊ ( beyondgrep.com ) โดยวิธีการchocolatey.org/packages/ack $FAVORITE_IDEหรือใช้ค้นหาง่ายในลักษณะไฟล์ใน
Brian Cline

21
class MyObject {
    Test main;

    public MyObject(Test t) {    
        main = t; 
    }

    protected void finalize() {
        main.ref = this; // let instance become reachable again
        System.out.println("This is finalize"); //test finalize run only once
    }
}

class Test {
    MyObject ref;

    public static void main(String[] args) {
        Test test = new Test();
        test.ref = new MyObject(test);
        test.ref = null; //MyObject become unreachable,finalize will be invoked
        System.gc(); 
        if (test.ref != null) System.out.println("MyObject still alive!");  
    }
}

====================================

ผลลัพธ์:

This is finalize

MyObject still alive!

=====================================

ดังนั้นคุณอาจสร้างอินสแตนซ์ที่ไม่สามารถเข้าถึงได้ซึ่งเข้าถึงได้ในขั้นตอนสุดท้าย


21
ด้วยเหตุผลบางอย่างรหัสนี้ทำให้ฉันคิดถึงภาพยนตร์ซอมบี้ คุณ GC วัตถุของคุณและจากนั้นก็ลุกขึ้นยืนอีกครั้ง ...
steveha

ข้างต้นเป็นรหัสที่ดี ฉันสงสัยว่าจะทำให้วัตถุเข้าถึงได้อีกครั้งในตอนนี้
lwpro2

15
ไม่ข้างต้นเป็นรหัสที่ไม่ดี มันเป็นตัวอย่างว่าทำไมการสรุปไม่ดี
Tony

โปรดจำไว้ว่าการเรียกใช้System.gc();ไม่ใช่การรับประกันว่าตัวรวบรวมขยะจะทำงานได้จริง มันขึ้นอยู่กับดุลยพินิจของ GC ในการทำความสะอาดฮีป (อาจยังมีฮีปฟรีเพียงพออยู่โดยรอบ ดังนั้นจึงเป็นเพียงคำขอต่ำต้อยโดยโปรแกรมเมอร์ "โปรดคุณได้ยินฉันออกและเรียกใช้?" และไม่ใช่คำสั่ง "ให้ทำงานทันทีที่ฉันพูด!" ขออภัยที่ใช้คำศัพท์ แต่มันบอกว่ามันตรงกับวิธีการทำงานของ GC ของ JVM
Roland

10

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

ฉันได้รับการเขียนโปรแกรมใน Java ตั้งแต่ 1.0 อัลฟ่า 3 (1995) และฉันยังไม่ได้แทนที่จบสำหรับอะไร ...


6

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


5

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

ดีที่สุดที่จะหลีกเลี่ยงด้วยข้อยกเว้นที่เป็นไปได้ของการวินิจฉัยการทดสอบ env

Eckel's Thinking in Java มีส่วนที่ดีในเรื่องนี้


4

อืมฉันเคยใช้มันเพื่อทำความสะอาดวัตถุที่ไม่ได้ถูกส่งกลับไปยังพูลที่มีอยู่

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


4

finalize()โปรดใช้ความระมัดระวังเกี่ยวกับสิ่งที่คุณทำใน โดยเฉพาะอย่างยิ่งถ้าคุณใช้มันสำหรับสิ่งต่าง ๆ เช่นการเรียก close () เพื่อให้แน่ใจว่าทรัพยากรจะถูกล้าง เราพบกับสถานการณ์หลายอย่างที่เรามีห้องสมุด JNI เชื่อมโยงกับโค้ดจาวาที่กำลังทำงานอยู่และในกรณีใด ๆ ที่เราใช้ finalize () เพื่อเรียกใช้เมธอด JNI เราจะได้รับ java heap คอร์รัปชั่นแย่มาก ความเสียหายไม่ได้เกิดจากรหัส JNI พื้นฐานตัวเองร่องรอยของหน่วยความจำทั้งหมดนั้นใช้ได้ในไลบรารีท้องถิ่น มันเป็นแค่ความจริงที่ว่าเราเรียกวิธีการ JNI จากการสรุป () เลย

นี่คือ JDK 1.5 ซึ่งยังคงใช้กันอย่างแพร่หลาย

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


3

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

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


2

แก้ไข: โอเคมันใช้งานไม่ได้จริง ๆ ฉันใช้มันและคิดว่าถ้ามันล้มเหลวบางครั้งมันก็โอเคสำหรับฉัน แต่มันก็ไม่ได้เรียกวิธีการสรุปครั้งเดียว

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

@Override
public void finalize()
{
    try {saveCache();} catch (Exception e)  {e.printStackTrace();}
}

public void saveCache() throws FileNotFoundException, IOException
{
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp"));
    out.writeObject(cache);
}

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

2

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

    โมฆะส่วนตัว addGlobalClickListener () {
        weakAwtEventListener = new WeakAWTEventListener (สิ่งนี้);

        Toolkit.getDefaultToolkit (). addAWTEventListener (weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK);
    }

    @แทนที่
    โมฆะที่ได้รับการป้องกันจะเสร็จสิ้น () พ่น Throwable {
        super.finalize ();

        if (weakAwtEventListener! = null) {
            . Toolkit.getDefaultToolkit () removeAWTEventListener (weakAwtEventListener);
        }
    }

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

0

iirc - คุณสามารถใช้วิธีการสุดท้ายเพื่อใช้กลไกการรวมกำไรสำหรับทรัพยากรราคาแพง - ดังนั้นพวกเขาจึงไม่ได้รับ GC ด้วย


0

ในฐานะที่เป็นบันทึกด้านข้าง:

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

ที่มา: finalize () เลิกใช้บน java-9


0

จำเป็นต้องปิดทรัพยากร (ไฟล์ซ็อกเก็ตสตรีม ฯลฯ ) เมื่อดำเนินการเสร็จแล้ว พวกเขามักจะมีclose()วิธีการที่เรามักเรียกในfinallyส่วนของtry-catchงบ บางครั้งfinalize()นักพัฒนาซอฟต์แวร์สามารถใช้งานได้ แต่ IMO นั้นไม่ใช่วิธีที่เหมาะสมเนื่องจากไม่มีการรับประกันว่าจะมีการเรียกใช้งานขั้นสุดท้ายเสมอ

ใน Java 7 เราได้คำสั่งลองกับทรัพยากรซึ่งสามารถใช้เช่น:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  // Processing and other logic here.
} catch (Exception e) {
  // log exception
} finally {
  // Just in case we need to do some stuff here.
}

ในตัวอย่างลองกับทรัพยากรจะปิดทรัพยากรBufferedReaderโดยอัตโนมัติด้วยclose()วิธีการเรียกใช้ ถ้าเราต้องการเรายังสามารถใช้งานCloseableในชั้นเรียนของเราเองและใช้ในลักษณะเดียวกัน IMO มันดูเรียบร้อยและเข้าใจง่ายขึ้น


0

โดยส่วนตัวแล้วฉันแทบจะไม่เคยใช้finalize()เลยยกเว้นในกรณีที่เกิดขึ้นได้ยากครั้งเดียว: ฉันสร้างคอลเลกชันประเภททั่วไปที่กำหนดเองและฉันเขียนfinalize()วิธีการที่กำหนดเองซึ่งทำสิ่งต่อไปนี้:

public void finalize() throws Throwable {
    super.finalize();
    if (destructiveFinalize) {
        T item;
        for (int i = 0, l = length(); i < l; i++) {
            item = get(i);
            if (item == null) {
                continue;
            }
            if (item instanceof Window) {
                ((Window) get(i)).dispose();
            }
            if (item instanceof CompleteObject) {
                ((CompleteObject) get(i)).finalize();
            }
            set(i, null);
        }
    }
}

( CompleteObjectเป็นอินเตอร์เฟซที่ผมทำที่ช่วยให้คุณระบุว่าคุณได้ใช้ไม่ค่อยนำมาใช้Objectวิธีการเช่น#finalize(), #hashCode()และ#clone())

ดังนั้นการใช้#setDestructivelyFinalizes(boolean)วิธีน้องสาวโปรแกรมที่ใช้คอลเลกชันของฉันสามารถ (ช่วย) รับประกันได้ว่าการทำลายการอ้างอิงไปยังคอลเลกชันนี้ยังทำลายการอ้างอิงถึงเนื้อหาและปิดหน้าต่างใด ๆ ที่อาจทำให้ JVM มีชีวิตอยู่โดยไม่ตั้งใจ ฉันคิดว่าจะหยุดเธรดใด ๆ ด้วยเช่นกัน แต่นั่นเป็นการเปิดกระป๋องใหม่ทั้งหมดของเวิร์ม


4
Calling finalize()บนCompleteObjectอินสแตนซ์เหมือนที่คุณทำในโค้ดข้างต้นแสดงให้เห็นว่ามันอาจจะได้รับการเรียกสองครั้งเป็น JVM ยังอาจก่อให้เกิดfinalize()ครั้งเดียววัตถุเหล่านี้จริงกลายเป็นไม่สามารถเข้าถึงซึ่งเนื่องจากตรรกะของการสรุปที่อาจจะก่อนfinalize()วิธีการของคอลเลกชันพิเศษของคุณ ถูกเรียกหรือแม้แต่พร้อมกันในเวลาเดียวกัน เนื่องจากฉันไม่เห็นมาตรการใด ๆ เพื่อรับรองความปลอดภัยของเธรดในวิธีการของคุณคุณดูเหมือนจะไม่ทราบเรื่องนี้ ...
Holger

0

รายการคำตอบที่ยอมรับได้ซึ่งสามารถปิดทรัพยากรได้ในระหว่างการดำเนินการขั้นสุดท้าย

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

ดังนั้นแม้ในสถานการณ์ดังกล่าวจะไม่สามารถทำการโทรติดต่อได้


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