มี destructor สำหรับ Java หรือไม่?


594

มี destructor สำหรับ Java หรือไม่? ฉันดูเหมือนจะไม่สามารถค้นหาเอกสารใด ๆ เกี่ยวกับเรื่องนี้ หากไม่มีฉันจะบรรลุผลเช่นเดียวกันได้อย่างไร

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

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

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


7
มีหน่วยความจำรั่วเท่านั้นถ้าคุณเก็บการอ้างอิงไปยังวัตถุที่คุณไม่ต้องการ เช่นมีข้อบกพร่องในโปรแกรมของคุณ ประชาคมโลกจะทำงานตามความต้องการ (บางครั้งเร็ว)
ปีเตอร์ Lawrey

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

1
@Kieveli JVM จะไม่เรียกใช้ GC ก่อนที่จะให้ข้อผิดพลาดหรือไม่
WVrock

4
ใช่มันจะดีถ้ามีตัวทำลายสำหรับ Java ที่จะทำลายมันครั้งเดียวสำหรับทุกคน
Tomáš Zato - Reinstate Monica

คำตอบ:


526

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

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

มีคำถามที่เกิดขึ้นในการสนทนาเชิงลึกของการสรุปเมื่อเร็ว ๆ นี้ดังนั้นจึงควรให้ความลึกมากขึ้นถ้าจำเป็น ...


5
"close ()" ในบริบทนี้อ้างถึงวิธีการใน java.lang.Autocloseable หรือไม่?
Sridhar Sarnobat

21
ไม่เปิดตัว AutoCloseable ใน Java 7 แต่การประชุม 'close ()' นั้นมีความยาวกว่านี้มาก
Jon Onstott

ทำไมคุณไม่สามารถคาดการณ์ได้เมื่อ (หรือแม้ว่า) วัตถุจะถูกทำลาย มีวิธีอื่นใดอีกที่จะคาดการณ์สิ่งนั้นได้?
dctremblay

@dctremblay การทำลายวัตถุทำได้โดยตัวรวบรวมขยะและตัวรวบรวมขยะอาจไม่สามารถทำงานได้ตลอดอายุการใช้งาน
Piro พูดว่า Reinstate Monica

4
โปรดทราบว่าfinalizeวิธีการเลิกใช้แล้วใน Java 9
Lii

124

มีลักษณะที่ได้ลองกับทรัพยากรคำสั่ง ตัวอย่างเช่น:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  System.out.println(br.readLine());
} catch (Exception e) {
  ...
} finally {
  ...
}

ที่นี่ทรัพยากรที่ไม่ต้องการอีกต่อไปได้รับการปลดปล่อยในBufferedReader.close()วิธีการ คุณสามารถสร้างคลาสของคุณเองที่นำไปใช้AutoCloseableและใช้ในลักษณะที่คล้ายกัน

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


12
ฉันประหลาดใจที่มีคะแนนน้อยมาก มันเป็นคำตอบที่แท้จริง
nurettin

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

14
แน่นอนนี่ไม่ใช่คำตอบที่แท้จริง มันเป็นไปไม่ได้ที่จะใช้โครงสร้างนี้เพื่อจัดการกับการทำลายของวัตถุเว้นแต่การก่อสร้างของวัตถุและการใช้ห่อหุ้มโดยสิ้นเชิงtryและถูกนำมาใช้เพื่อบังคับให้การเรียกร้องให้finally obj.finalize()และแม้แต่การตั้งค่านี้ก็ไม่สามารถแก้ปัญหาที่เกิดจาก OP: โปรแกรมทำลายวัตถุกลางเรียกโดยปุ่ม "รีเซ็ต"
7yl4r

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

1
@nurettin Java 7 เพิ่งจะออกมา 3 เดือนเมื่อคำถามถูกถามถ้ามันช่วยให้เข้าใจมากขึ้น
corsiKa

110

ไม่นี่ไม่มี destructors เหตุผลคือวัตถุ Java ทั้งหมดได้รับการจัดสรรฮีปและรวบรวมขยะ หากไม่มีการยกเลิกการจัดสรรอย่างชัดเจน (เช่นโอเปอเรเตอร์การลบของ C ++) จะไม่มีวิธีที่เหมาะสมในการติดตั้ง destructor จริง

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

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


4
ขอบคุณสำหรับการกล่าวถึงแหล่งข้อมูลพื้นเมือง - นี่เป็นพื้นที่หนึ่งที่วิธีการ "เหมือนตัวทำลาย" มีประโยชน์
นาธานออสมัน

ใช่ฉันประสบปัญหาเดียวกันในขณะนี้ด้วยการเพิ่มทรัพยากร / การจัดการที่จัดสรรผ่านการโทรแบบดั้งเดิมไปยัง C ++
nikk

@ddimitrov ในทางทฤษฎีแล้ว java สามารถใช้การจัดสรรคืนอย่างชัดเจนได้หรือไม่ หรือนี่คือความขัดแย้งเชิงตรรกะ?
mils

1
@mils ไร้เดียงสาการใช้การจัดสรรคืนอย่างชัดเจนอาจทำลายสมมติฐานของ Java ที่การอ้างอิงใด ๆ ชี้ไปยังวัตถุสด คุณสามารถทำซ้ำพอยน์เตอร์ทั้งหมดและลบล้างนามแฝง แต่มีราคาแพงกว่า GC หรือคุณอาจลองใช้ระบบแบบเส้นตรง (ดูที่ "ความเป็นเจ้าของ" ในแบบ Rust) แต่นั่นเป็นการเปลี่ยนแปลงภาษาที่สำคัญ มีตัวเลือกอื่นด้วยเช่นกัน (ดูหน่วยความจำที่กำหนดขอบเขตของ JavaRT ฯลฯ ) แต่โดยทั่วไปการจัดสรรคืนอย่างชัดเจนไม่เหมาะสมกับภาษา Java
ddimitrov

31

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

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

Resource r = new Resource();
try {
    //work
} finally {
    r.dispose();
}

ความพยายามในการใช้วัตถุที่จำหน่ายควรโยนข้อยกเว้นรันไทม์ (ดูIllegalStateException )


แก้ไข:

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

โดยทั่วไปสิ่งที่คุณต้องทำคือยกเลิกการอ้างอิงวัตถุ - อย่างน้อยนี่คือวิธีที่มันควรจะทำงาน หากคุณกังวลเกี่ยวกับการรวบรวมขยะให้ตรวจสอบJava SE 6 HotSpot [tm] การปรับแต่งการรวบรวมขยะเสมือนของเครื่องจักร (หรือเอกสารเทียบเท่าสำหรับรุ่น JVM ของคุณ)


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

1
ลอง .. ในที่สุดก็เป็นวิธีที่ถูกต้องและแนะนำใช่ไหม สมมติว่าก่อนหน้านี้ฉันเคยเรียกใช้วิธีการเนทีฟในขั้นตอนสุดท้าย () ฉันจะย้ายการโทรไปยังประโยคสุดท้ายได้หรือไม่? class Resource { finalize() { destroy(); } protected native void destroy(); } class Alt_Resource { try (Resource r = new Resource()) { // use r } finalize { r.destroy(); }
aashima

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

21

เมื่อเปิดตัว Java 1.7 คุณจะมีทางเลือกเพิ่มเติมในการใช้try-with-resourcesบล็อก ตัวอย่างเช่น,

public class Closeable implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("closing..."); 
    }
    public static void main(String[] args) {
        try (Closeable c = new Closeable()) {
            System.out.println("trying..."); 
            throw new Exception("throwing..."); 
        }
        catch (Exception e) {
            System.out.println("catching..."); 
        }
        finally {
            System.out.println("finalizing..."); 
        } 
    }
}

หากคุณดำเนินการชั้นนี้c.close()จะถูกดำเนินการเมื่อtryบล็อกถูกทิ้งไว้และก่อนที่จะดำเนินการcatchและfinallyบล็อก แตกต่างจากในกรณีของfinalize()วิธีการที่close()รับประกันว่าจะดำเนินการ อย่างไรก็ตามไม่จำเป็นต้องดำเนินการอย่างชัดเจนในfinallyข้อ


ถ้าเราไม่ได้ใช้การลองกับทรัพยากร? ฉันคิดว่าเราสามารถโทรเข้าใกล้จบ () เพียงเพื่อให้แน่ใจว่าได้รับการเรียกว่าปิด
ชินโต

3
@shintoZ เท่าที่ผมอ่านในข้างต้นคำตอบมีการรับประกันไม่มีfinalize()การดำเนินการ
ราวกับ Mushtaq

14

ฉันเห็นด้วยอย่างยิ่งกับคำตอบอื่นโดยบอกว่าไม่ต้องพึ่งพาการดำเนินการขั้นสุดท้าย

นอกเหนือจากการลองบล็อกในที่สุดคุณอาจใช้Runtime # addShutdownHook (แนะนำใน Java 1.3) เพื่อดำเนินการสะสางขั้นสุดท้ายในโปรแกรมของคุณ

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

ประโยชน์จากการนี้จะมีพฤติกรรมโครงสร้างคู่อย่างอิสระจากส่วนที่เหลือของโปรแกรมของคุณ


addShutdownHook ถูกนำมาใช้ใน Java 1.3 อย่างไรก็ตามมีให้ฉันใน 1.5 :) ดูสิ่งนี้: stackoverflow.com/questions/727151/…
skiphoppy

1
หากคุณใช้ปุ่ม "ยุติ" สีแดงใน Eclipse - JVM ทั้งหมดจะถูกทำลายทันทีตะขอขอปิดจะไม่ถูกเรียกอย่างสง่างาม หมายความว่าคุณอาจเห็นพฤติกรรมที่แตกต่างระหว่างการพัฒนาและการผลิตหากคุณพัฒนาโดยใช้ eclipse
Hamy

11

ไม่java.lang.Object#finalizeใกล้เคียงที่สุดที่คุณจะได้รับ

อย่างไรก็ตามเมื่อมีการเรียก (และถ้า) จะไม่รับประกัน
ดู:java.lang.Runtime#runFinalizersOnExit(boolean)


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

@antred นักพัฒนาของภาษา Java เห็นด้วย ฉันเดาว่าสำหรับบางคนมันเป็นครั้งแรกที่พวกเขาออกแบบภาษาการเขียนโปรแกรมและสภาพแวดล้อมรันไทม์ที่มีการรวบรวมขยะ สิ่งที่เข้าใจได้น้อยกว่าคือสาเหตุที่ภาษาที่มีการจัดการอื่น ๆคัดลอกแนวคิดดังกล่าวในแต่ละครั้งเมื่อมีการเข้าใจแล้วว่าแนวคิดนี้เป็นแนวคิดที่ไม่ดี
Holger

7

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

คุณสามารถได้รับการแจ้งเตือนหลังจากวัตถุถูกทำลายโดยใช้ java.lang.ref.PhantomReference (จริง ๆ แล้วว่าการถูกทำลายอาจไม่ถูกต้องเล็กน้อย แต่ถ้า phantom อ้างอิงถึงวัตถุนั้นอยู่ในคิวแล้วมันจะไม่สามารถกู้คืนได้อีกต่อไป สิ่งเดียวกัน). การใช้งานทั่วไปคือ:

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

นอกจากนี้ยังมีการสรุป () ซึ่งดูเหมือนเป็นตัวทำลายล้าง แต่ไม่ทำงานเหมือนอย่างใดอย่างหนึ่ง มันมักจะไม่ใช่ตัวเลือกที่ดี


ทำไมต้อง PhantomReference แทนที่จะเป็น WeakReference?
uckelman

2
@uckelman: ถ้าสิ่งที่คุณต้องการคือการแจ้งเตือนจากนั้น PhantomReference ก็ทำหน้าที่นี้ได้มันค่อนข้างจะเป็นสิ่งที่ออกแบบมา ซีแมนทิกส์เพิ่มเติมของ WeakReference ไม่ต้องการที่นี่และ ณ จุดที่ ReferenceQueue ของคุณได้รับการแจ้งเตือนคุณไม่สามารถกู้คืนวัตถุผ่าน WeakReference ได้อีกต่อไปดังนั้นเหตุผลเดียวที่ใช้คือเพื่อบันทึกว่าต้องจำไว้ว่า PhantomReference มีอยู่ งานพิเศษใด ๆ ที่ WeakReference ทำนั้นอาจจะเล็กน้อย แต่ทำไมต้องกังวลกับเรื่องนี้?
Steve Jessop

ขอบคุณสำหรับคำใบ้ที่ PhantomReference มันไม่สมบูรณ์แบบ แต่ยังดีกว่าไม่มีอะไร
foo

@SteveJessop สิ่งที่ "งานพิเศษ" คุณคิดว่ามีการอ้างอิงที่อ่อนแอเมื่อเทียบกับการอ้างอิงผี?
Holger

6

finalize()ฟังก์ชั่นเตาเผา

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

นอกจากนี้ก็จะใช้เวลามากกว่าหนึ่ง GC วัตถุ deallocate finalize()ที่มี

คุณควรพยายามทำความสะอาดในสถานที่ตรรกะในรหัสของคุณโดยใช้try{...} finally{...}คำสั่ง!


5

ฉันเห็นด้วยกับคำตอบส่วนใหญ่

คุณไม่ควรพึ่งพาอย่างใดอย่างหนึ่งfinalizeหรือShutdownHook

จบ

  1. JVM ไม่รับประกันว่าfinalize()จะใช้วิธีนี้เมื่อใด

  2. finalize()ถูกเรียกเพียงครั้งเดียวโดยเธรด GC หากวัตถุฟื้นคืนตัวเองจากวิธีการสรุปแล้วfinalizeจะไม่ถูกเรียกอีกครั้ง

  3. ในแอปพลิเคชันของคุณคุณอาจมีบางวัตถุที่มีชีวิตซึ่งไม่เคยเรียกใช้การรวบรวมขยะ

  4. สิ่งใดก็ตามExceptionที่ถูกโยนโดยวิธีการปิดท้ายจะถูกละเว้นโดยเธรด GC

  5. System.runFinalization(true)และRuntime.getRuntime().runFinalization(true)วิธีการเพิ่มความน่าจะเป็นของfinalize()วิธีการเรียกใช้แต่ตอนนี้ทั้งสองวิธีได้รับการคัดค้าน วิธีการเหล่านี้มีอันตรายมากเนื่องจากการขาดความปลอดภัยของเธรดและการสร้างการหยุดชะงักที่เป็นไปได้

shutdownHooks

public void addShutdownHook(Thread hook)

ลงทะเบียน hook การปิดเครื่องเสมือนใหม่

เครื่องเสมือน Java ปิดการทำงานเพื่อตอบสนองต่อเหตุการณ์สองประเภท:

  1. โปรแกรมออกตามปกติเมื่อเธรดที่ไม่ใช่ daemon ออกแล้วหรือเมื่อมีSystem.exitการเรียกใช้เมธอดexit (อย่างเท่าเทียมกัน) หรือ
  2. เครื่องเสมือนถูกยกเลิกในการตอบสนองต่อการขัดจังหวะโดยผู้ใช้เช่นการพิมพ์ ^ C หรือเหตุการณ์ทั่วทั้งระบบเช่นการออกจากระบบของผู้ใช้หรือการปิดระบบ
  3. เบ็ดปิดเป็นเพียงกระทู้เริ่มต้น แต่ไม่ได้เริ่มต้น เมื่อเครื่องเสมือนเริ่มต้นการปิดเครื่องมันจะเริ่ม hooks การปิดที่ลงทะเบียนไว้ทั้งหมดตามลำดับที่ไม่ระบุและให้มันทำงานพร้อมกัน เมื่อ hooks ทั้งหมดเสร็จสิ้นแล้วจะเรียกใช้ finalizers ที่ยังไม่ได้เรียกใช้ทั้งหมดหากเปิดใช้งาน finalization-on-exit แล้ว
  4. ในที่สุดเครื่องเสมือนจะหยุด โปรดทราบว่า daemon เธรดจะยังคงทำงานต่อไปในระหว่างลำดับการปิดเช่นเดียวกับเธรดที่ไม่ใช่ daemon หากการปิดระบบเริ่มต้นด้วยการเรียกใช้เมธอด exit
  5. ตะขอปิดเครื่องควรทำงานให้เสร็จอย่างรวดเร็ว เมื่อโปรแกรมเรียกออกจากความคาดหมายคือเครื่องเสมือนจะปิดและออกโดยทันที

    แต่แม้กระทั่งเอกสารของออราเคิลก็อ้างว่า

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

สิ่งนี้เกิดขึ้นเมื่อเครื่องเสมือนถูกยกเลิกจากภายนอกเช่นมีSIGKILLสัญญาณบน Unix หรือการTerminateProcessโทรบน Microsoft Windows เครื่องเสมือนอาจยกเลิกหากวิธีเนทีฟมีความผิดพลาดเช่นการทำลายโครงสร้างข้อมูลภายในหรือพยายามเข้าถึงหน่วยความจำที่ไม่มีอยู่ หากเครื่องเสมือนยกเลิกจะไม่มีการรับประกันใด ๆ เกี่ยวกับว่าตะขอการปิดระบบใด ๆ จะถูกเรียกใช้หรือไม่

สรุป :ใช้try{} catch{} finally{}บล็อกอย่างเหมาะสมและปล่อยทรัพยากรที่สำคัญใน finally(}บล็อก ในช่วงการเปิดตัวของทรัพยากรในfinally{}บล็อกจับและExceptionThrowable


4

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


3

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


1
นี่เป็นคำตอบที่ถูกต้องเมื่อทรัพยากรมีปัญหามีเจ้าของเดียวและไม่เคยมีการอ้างอิงถึงมันว่า "ขโมย" โดยรหัสอื่น
Steve Jessop

3

มีคำอธิบายประกอบ@Cleanupในลอมบอกซึ่งส่วนใหญ่คล้ายกับ C ++ destructors คือ:

@Cleanup
ResourceClass resource = new ResourceClass();

เมื่อประมวลผล (ณ เวลารวบรวม) ลอมบอกจะแทรกtry-finallyบล็อกที่เหมาะสมเพื่อให้resource.close()มีการเรียกใช้เมื่อการดำเนินการออกจากขอบเขตของตัวแปร คุณสามารถระบุวิธีอื่นในการปล่อยทรัพยากรได้อย่างชัดเจนเช่นresource.dispose():

@Cleanup("dispose")
ResourceClass resource = new ResourceClass();

2
ข้อได้เปรียบที่ฉันเห็นคือจะมีการทำรังน้อยลง (ซึ่งอาจมีความสำคัญหากคุณมีวัตถุจำนวนมากที่ต้องการ "ทำลาย")
Alexey

บล็อก try-with-resource สามารถมีหลายทรัพยากรได้ในนัดเดียว
Alexander - Reinstate Monica

1
แต่มันไม่สามารถมีคำแนะนำระหว่างพวกเขา
Alexey

ธรรม ฉันคิดว่าคำแนะนำคือชอบลองกับทรัพยากรแม้ว่าจะมีหลายทรัพยากรเว้นแต่จำเป็นต้องมีคำแนะนำระหว่างพวกเขาที่บังคับให้คุณสร้างบล็อกลองกับทรัพยากรใหม่ (เพิ่มการซ้อน) แล้วใช้@Cleanup
Alexander - Reinstate Monica

2

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


2

หลายคำตอบที่ดีที่นี่ แต่มีบางข้อมูลเพิ่มเติมเกี่ยวกับเหตุผลที่คุณควรหลีกเลี่ยงการใช้จบ ()

หาก JVM ออกจากเนื่องจากSystem.exit()หรือRuntime.getRuntime().exit()finalizers จะไม่ถูกเรียกใช้ตามค่าเริ่มต้น จากJavadoc สำหรับ Runtime.exit () :

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

คุณสามารถโทรได้System.runFinalization()แต่จะทำให้ "เป็นความพยายามที่ดีที่สุดในการทำให้การสรุปที่โดดเด่นทั้งหมดเสร็จสมบูรณ์" ไม่ใช่การรับประกัน

มีSystem.runFinalizersOnExit()วิธีการ แต่ไม่ได้ใช้ - มันไม่ปลอดภัยเลิกใช้มานานแล้ว


1

หากคุณกำลังเขียน Java Applet คุณสามารถแทนที่เมธอด "destroy ()" ของ Applet มันคือ...

 * Called by the browser or applet viewer to inform
 * this applet that it is being reclaimed and that it should destroy
 * any resources that it has allocated. The stop() method
 * will always be called before destroy().

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


1

แค่คิดถึงคำถามต้นฉบับ ... ซึ่งฉันคิดว่าเราสามารถสรุปจากคำตอบที่ได้เรียนรู้ทั้งหมดและจากจาวา Effective Effective ที่จำเป็นของ Bloch ข้อ 7 "หลีกเลี่ยงการเข้ารอบสุดท้าย" ค้นหาคำตอบที่ถูกต้องตามกฎหมายในลักษณะที่ ไม่เหมาะสมกับภาษา Java ... :

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

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

หากวัตถุใด ๆ เหล่านี้Closeable(หรือไม่ แต่มีcloseวิธีการ) คุณสามารถใส่ไว้ในBagplaypen ในขณะที่พวกเขาถูกสร้างขึ้น (และอาจเปิด) และการกระทำสุดท้ายของ accessor ก่อนที่จะตัด playpen จะไป ผ่านการCloseablesปิดทั้งหมด...

โค้ดอาจมีลักษณะดังนี้:

accessor.getPlaypen().closeCloseables();
accessor.setPlaypen( new Playpen() );

closeCloseablesอาจเป็นวิธีการบล็อกอาจเกี่ยวข้องกับ latch (เช่นCountdownLatch) เพื่อจัดการ (และรอตามความเหมาะสม) ใด ๆRunnables/ Callablesในเธรดใด ๆ ที่เฉพาะเจาะจงเพื่อPlaypenสิ้นสุดลงตามความเหมาะสมโดยเฉพาะในเธรด JavaFX


0

แม้ว่าจะมีความก้าวหน้าอย่างมากในเทคโนโลยี GC ของ Java แต่คุณยังต้องระวังการอ้างอิงของคุณ มีหลายกรณีที่มีรูปแบบการอ้างอิงที่น่าสนใจที่จริงแล้วหนูรังอยู่ใต้ฝากระโปรง

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


0

ไม่มีคลาส destructor ที่แน่นอนใน Java คลาสถูกทำลายใน java โดยอัตโนมัติโดยตัวรวบรวมขยะ แต่คุณสามารถทำได้โดยใช้คำสั่งด้านล่าง แต่ไม่เหมือนกัน:

จบ ()

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


0

ไม่มี Java ไม่มี destructors ใด ๆ เหตุผลหลักที่อยู่เบื้องหลังใน Java คือ Garbage Collector ที่ทำงานอย่างอดทนในพื้นหลังเสมอและวัตถุทั้งหมดจะทำในหน่วยความจำฮีปซึ่งเป็นสถานที่ที่ทำงาน GC ใน c ++ ที่นั่นเรา ต้องเรียกฟังก์ชันลบอย่างชัดเจนเนื่องจากไม่มีตัวรวบรวมขยะ


-3

ฉันเคยจัดการกับ C ++ เป็นหลักและนั่นคือสิ่งที่นำฉันไปสู่การค้นหาผู้ทำลายล้างด้วยเช่นกัน ตอนนี้ฉันใช้ JAVA บ่อยครั้ง สิ่งที่ฉันทำและอาจไม่ใช่กรณีที่ดีที่สุดสำหรับทุกคน แต่ฉันใช้ตัวทำลายระบบด้วยการรีเซ็ตค่าทั้งหมดเป็น 0 หรือมีค่าเริ่มต้นผ่านฟังก์ชั่น

ตัวอย่าง:

public myDestructor() {

variableA = 0; //INT
variableB = 0.0; //DOUBLE & FLOAT
variableC = "NO NAME ENTERED"; //TEXT & STRING
variableD = false; //BOOL

}

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

ฉันรู้ว่าฉันไม่ใช่โปรแกรมเมอร์ Java ที่ดีที่สุด แต่ดูเหมือนจะใช้งานได้สำหรับฉัน


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

5
มันไม่ได้ผิดอะไรมากนักเพราะมันไร้จุดหมาย - นั่นคือไม่ทำอะไรเลย หากโปรแกรมของคุณต้องการการตั้งค่าประเภท raw เพื่อให้ทำงานได้อย่างถูกต้องอินสแตนซ์ของคลาสของคุณจะถูกกำหนดขอบเขตอย่างไม่ถูกต้องหมายความว่าคุณอาจกำหนดคุณสมบัติของวัตถุที่มีอยู่ให้กับคุณสมบัติของวัตถุใหม่โดยไม่ต้องสร้างวัตถุใหม่ ()
simo.3792

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