เมื่อไหร่จะทำการ finalize () วิธีการใน Java?


330

ฉันจำเป็นต้องทราบเมื่อมีการใช้วิธีการที่เรียกว่าในfinalize() JVMฉันสร้างคลาสทดสอบซึ่งเขียนลงในไฟล์เมื่อมีfinalize()การเรียกเมธอดโดยลบล้างมัน มันไม่ได้ดำเนินการ ใครช่วยบอกฉันได้ว่าทำไมมันถึงไม่ทำงาน?


34
เช่นเดียวกับข้อความด้าน: การสรุปถูกทำเครื่องหมายว่าเลิกใช้แล้วใน Java 9
Reg


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

คำตอบ:


273

โดยทั่วไปไม่ควรพึ่งพา finalize()ทำความสะอาดใด ๆ

ตามที่Javadoc (ซึ่งมันจะคุ้มค่าการอ่าน) ก็คือ:

เรียกว่าโดยตัวรวบรวมขยะบนวัตถุเมื่อการรวบรวมขยะกำหนดว่าไม่มีการอ้างอิงไปยังวัตถุ

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

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


19
ในคำอื่น ๆ (เพียงชี้แจงสำหรับผู้อ่านในอนาคต) มันไม่เคยถูกเรียกในชั้นเรียนหลักเช่นเมื่อปิดชั้นเรียนหลักไม่จำเป็นต้องเก็บขยะ ระบบปฏิบัติการล้างข้อมูลทุกอย่างที่แอพใช้อยู่ดี
Mark Jeronimus

113
"ไม่ใช่วิธีที่ดีที่สุดที่จะใช้ ... เว้นแต่จะมีบางอย่างที่คุณต้องการสำหรับ" - เอ่อประโยคนั้นใช้กับทุกอย่างได้ 100% ดังนั้นจึงไม่มีประโยชน์ คำตอบของโจอาคิมซาวเออร์ดีกว่ามาก
BT

1
@ Zom-B ตัวอย่างของคุณมีประโยชน์สำหรับการทำให้กระจ่างแจ้ง แต่เพื่อให้เป็นคนอวดดีคงเป็นไปได้ว่ามันจะถูกเรียกบนคลาสหลักถ้าคลาสหลักสร้างเธรดที่ไม่ใช่ daemon แล้วส่งคืนหรือไม่
Tom G

3
ดังนั้นสถานการณ์ที่finalizeจะมีประโยชน์คืออะไร?
nbro

3
@ MarkJeronimus - จริงๆแล้วมันไม่เกี่ยวข้องเลย finalize()วิธีการสำหรับชั้นหลักจะเรียกเมื่อ >> << อินสแตนซ์ของชั้นที่มีการเก็บขยะไม่ได้เมื่อยุติวิธีการหลัก นอกจากนี้คลาสหลักอาจเป็นที่เก็บขยะก่อนที่แอปพลิเคชันจะเสร็จสิ้น เช่นในแอพแบบมัลติเธรดที่เธรด "main" สร้างเธรดอื่นแล้วส่งคืน (ในทางปฏิบัติต้องใช้ classloader ที่ไม่เป็นมาตรฐาน .... )
Stephen C

379

finalizeวิธีการที่เรียกว่าเมื่อวัตถุเป็นเรื่องเกี่ยวกับที่จะได้รับการเก็บขยะ ซึ่งอาจเป็นเมื่อใดก็ได้หลังจากที่มีสิทธิ์ในการรวบรวมขยะ

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

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

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


2
@Rajesh ไม่มันไม่ใช่ปัญหา "อายุขัย" คุณสามารถใส่โปรแกรมของคุณในวงวนไม่ จำกัด (เป็นเวลาหลายปี) และหากไม่จำเป็นต้องใช้ตัวรวบรวมข้อมูลขยะโปรแกรมจะไม่ทำงาน
ChrisCantrell

5
@VikasVerma: การทดแทนที่สมบูรณ์แบบคืออะไร: คุณไม่ควรต้องการมัน กรณีเดียวที่เหมาะสมก็คือถ้าคลาสของคุณจัดการทรัพยากรภายนอกบางอย่าง (เช่นการเชื่อมต่อ TCP / IP ไฟล์ ... ทุกอย่างที่ Java GC ไม่สามารถจัดการได้) ในกรณีเหล่านั้นClosableอินเทอร์เฟซ (และแนวคิดเบื้องหลัง) อาจเป็นสิ่งที่คุณต้องการ: ทำการ.close()ปิด / ทิ้งทรัพยากรและต้องการให้ผู้ใช้ในชั้นเรียนของคุณเรียกมันในเวลาที่เหมาะสม คุณอาจต้องการเพิ่มfinalizeวิธีการ "เพียงบันทึก" แต่นั่นจะเป็นเครื่องมือการดีบักมากกว่าการแก้ไขที่แท้จริง (เพราะไม่น่าเชื่อถือเพียงพอ)
Joachim Sauer

4
@ Dragononborn: นั่นเป็นคำถามที่แตกต่างกันมากและควรถามแยกต่างหาก มี hooks การปิดระบบ แต่จะไม่รับประกันหาก JVM ปิดการทำงานกะทันหัน (aka ล่ม) แต่การค้ำประกันของพวกเขานั้นแข็งแกร่งกว่าการผ่านเข้ารอบสุดท้าย (และปลอดภัยกว่าเช่นกัน)
Joachim Sauer

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

2
Finalizers บางครั้งสามารถบันทึกวัน ... ฉันมีกรณีที่ห้องสมุดบุคคลที่สามใช้ FileInputStream ไม่เคยปิด รหัสของฉันเรียกรหัสห้องสมุดแล้วพยายามย้ายไฟล์ล้มเหลวเนื่องจากยังเปิดอยู่ ฉันต้องบังคับให้มีการเรียกSystem.gc()ใช้ FileInputStream :: finalize () จากนั้นฉันสามารถย้ายไฟล์ได้
Elist

73
protected void finalize() throws Throwable {}
  • ทุกคลาสสืบทอดfinalize()วิธีการจาก java.lang.Object
  • เมธอดถูกเรียกใช้โดยตัวรวบรวมขยะเมื่อพิจารณาว่าไม่มีการอ้างอิงไปยังวัตถุอีกต่อไป
  • วิธีการสรุปวัตถุไม่มีการดำเนินการ แต่อาจถูกแทนที่โดยคลาสใด ๆ
  • ปกติแล้วมันควรจะถูกแทนที่เพื่อล้างทรัพยากรที่ไม่ใช่ Java เช่นการปิดไฟล์
  • ถ้า overridding มันคือการเขียนโปรแกรมที่ดีที่จะใช้คำสั่งลองจับและในที่สุดก็จะโทรเสมอfinalize() super.finalize()นี่เป็นมาตรการด้านความปลอดภัยเพื่อให้แน่ใจว่าคุณไม่พลาดการปิดรีซอร์สที่ใช้โดยคลาสที่เรียกออบเจ็กต์

    protected void finalize() throws Throwable {
         try {
             close();        // close open files
         } finally {
             super.finalize();
         }
     }
  • ข้อยกเว้นใด ๆ ที่ถูกโยนโดยfinalize()ในระหว่างการรวบรวมขยะหยุดการสรุป แต่จะถูกละเว้น

  • finalize() ไม่เคยวิ่งมากกว่าหนึ่งครั้งบนวัตถุใด ๆ

ยกมาจาก: http://www.janeg.ca/scjp/gc/finalize.html

คุณสามารถตรวจสอบบทความนี้:


25
บทความ JavaWorld ที่คุณลิงก์ไปนั้นมาจากปี 1998 และมีคำแนะนำที่น่าสนใจโดยเฉพาะคำแนะนำที่เรียกว่า System.runFinalizersOnExit () หนึ่งครั้งเพื่อให้แน่ใจว่า finalizers จะทำงานก่อนที่ JVM จะออก วิธีการนั้นเลิกใช้แล้วในขณะนี้พร้อมกับความคิดเห็น 'วิธีนี้ไม่ปลอดภัยโดยเนื้อแท้ มันอาจส่งผลให้ finalizers ถูกเรียกบนวัตถุที่มีชีวิตในขณะที่เธรดอื่น ๆ กำลังจัดการวัตถุเหล่านั้นพร้อมกันส่งผลให้เกิดพฤติกรรมผิดปกติหรือการหยุดชะงัก ' ดังนั้นฉันจะไม่ทำอย่างนั้น
เกร็ก Chabala

3
เนื่องจาก runFinalizerOnExit () ไม่ปลอดภัยสำหรับเธรดสิ่งที่สามารถทำได้คือ Runtime.getRuntime () addShutdownHook (เธรดใหม่ () {โมฆะสาธารณะที่รัน () {destroyMyEnclosingClass ();}}); ในตัวสร้างของคลาส
Ustaman Sangat

2
@Ustaman Sangat นั่นเป็นวิธีที่ทำได้ แต่โปรดจำไว้ว่านี่เป็นการอ้างอิงถึงอินสแตนซ์ของคุณจากการปิดระบบตะขอซึ่งรับประกันได้ว่าห้องเรียนของคุณจะไม่ถูกเก็บขยะ กล่าวอีกนัยหนึ่งมันเป็นความจำรั่ว
pieroxy

1
@ เพียร์ซีในขณะที่ฉันเห็นด้วยกับคนอื่น ๆ ที่นี่เกี่ยวกับการไม่ใช้ finalize () สำหรับอะไรฉันไม่เห็นว่าทำไมจะต้องมีการอ้างอิงจากเบ็ดปิด อาจมีการอ้างอิงที่อ่อนนุ่ม
Ustaman Sangat

25

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

สิ่งที่คุณอาจต้องการคือการรวมกันfinallyและวิธีการล้างข้อมูลเช่นเดียวกับใน:

MyClass myObj;

try {
    myObj = new MyClass();

    // ...
} finally {

    if (null != myObj) {
        myObj.cleanup();
    }
}

20

ตรวจสอบ Java ที่มีประสิทธิภาพหน้า 2 รุ่น 27 รายการที่ 7: หลีกเลี่ยงขั้นตอนสุดท้าย

Finalizers คาดเดาไม่ได้มักเป็นอันตรายและไม่จำเป็นโดยทั่วไป ไม่เคยทำอะไรที่สำคัญในการเข้ารอบสุดท้าย ไม่เคยขึ้นอยู่กับ finalizer เพื่ออัปเดตสถานะถาวรที่สำคัญ

หากต้องการยุติทรัพยากรให้ลองใช้แทนแทน:

// try-finally block guarantees execution of termination method
Foo foo = new Foo(...);
try {
    // Do what must be done with foo
    ...
} finally {
    foo.terminate(); // Explicit termination method
}

3
หรือใช้ลองกับทรัพยากร
กาเบรียลการ์เซีย

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

16

เมื่อเป็นfinalize()วิธีการที่เรียกว่าใน Java?

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

  • หากวัตถุไม่สามารถเข้าถึงไม่ได้finalize()จะไม่มีการเรียกใช้วัตถุนั้น

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

  • อาจใช้เวลามากกว่าหนึ่งรอบ GC ก่อนที่ GC จะกำหนดว่าไม่สามารถเข้าถึงวัตถุที่เฉพาะเจาะจงได้ (Java GCs มักจะเป็นนักสะสม "generational" ... )

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

(สเปค JVM จริง ๆ แล้วอนุญาตให้ JVM ไม่เคยทำรัน finalizers ... โดยที่มันไม่ได้เรียกคืนพื้นที่ที่ใช้โดยวัตถุ JVM ที่ใช้งานในลักษณะนี้จะพิการ / ไร้ประโยชน์ แต่พฤติกรรมนี้คือ "อนุญาต" .)

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

การใช้อย่างถูกต้องตามกฎหมายสำหรับการจัดทำขั้นสุดท้ายเท่านั้นคือการล้างทรัพยากรที่เกี่ยวข้องกับวัตถุที่รหัสแอปพลิเคชันสูญหาย ถึงอย่างนั้นคุณควรพยายามเขียนรหัสแอปพลิเคชันเพื่อไม่ให้วัตถุหายไปตั้งแต่แรก (ตัวอย่างเช่นใช้ Java 7+ ลองกับทรัพยากรเพื่อให้แน่ใจว่าclose()ถูกเรียกเสมอ ... )


ฉันสร้างคลาสทดสอบซึ่งเขียนลงในไฟล์เมื่อมีการเรียกใช้เมธอด finalize () โดยการแทนที่ มันไม่ได้ดำเนินการ ใครช่วยบอกฉันได้ว่าทำไมมันถึงไม่ทำงาน?

มันยากที่จะพูด แต่มีความเป็นไปได้ไม่กี่:

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

10

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

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

ตัวอย่าง

class Car {

    int maxspeed;

    Car() {
        maxspeed = 70;
    }

    protected void finalize() {

    // Originally finalize method does nothing, but here we override finalize() saying it to print some stmt
    // Calling of finalize is uncertain. Difficult to observe so we force JVM to call it by System.gc(); GarbageCollection

        System.out.println("Called finalize method in class Car...");
    }
}

class Bike {

    int maxspeed;

    Bike() {
        maxspeed = 50;
    }

    protected void finalize() {
        System.out.println("Called finalize method in class Bike...");
    }
}

class Example {

    public static void main(String args[]) {
        Car c = new Car();
        c = null;    // if c weren`t null JVM wouldn't be certain it's cleared or not, null means has no future use or no longer in use hence clears it
        Bike b = new Bike();
        System.gc();    // should clear c, but not b
        for (b.maxspeed = 1; b.maxspeed <= 70; b.maxspeed++) {
            System.out.print("\t" + b.maxspeed);
            if (b.maxspeed > 50) {
                System.out.println("Over Speed. Pls slow down.");
            }
        }
    }
}

เอาท์พุต

    Called finalize method in class Car...
            1       2       3       4       5       6       7       8       9
    10      11      12      13      14      15      16      17      18      19
    20      21      22      23      24      25      26      27      28      29
    30      31      32      33      34      35      36      37      38      39
    40      41      42      43      44      45      46      47      48      49
    50      51Over Speed. Pls slow down.
            52Over Speed. Pls slow down.
            53Over Speed. Pls slow down.
            54Over Speed. Pls slow down.
            55Over Speed. Pls slow down.
            56Over Speed. Pls slow down.
            57Over Speed. Pls slow down.
            58Over Speed. Pls slow down. 
            59Over Speed. Pls slow down.
            60Over Speed. Pls slow down.
            61Over Speed. Pls slow down.
            62Over Speed. Pls slow down.
            63Over Speed. Pls slow down.
            64Over Speed. Pls slow down.
            65Over Speed. Pls slow down.
            66Over Speed. Pls slow down.
            67Over Speed. Pls slow down.
            68Over Speed. Pls slow down.
            69Over Speed. Pls slow down.
            70Over Speed. Pls slow down.

หมายเหตุ - แม้หลังจากพิมพ์ไม่เกิน 70 และหลังจากที่วัตถุ b ไม่ได้ถูกใช้ในโปรแกรมมีความไม่แน่นอนที่ b ถูกล้างหรือไม่ได้รับจาก JVM ตั้งแต่ "วิธีการที่เรียกว่า finalize ใน class Bike ... " ไม่ถูกพิมพ์


10
การโทรSystem.gc();ไม่ใช่การรับประกันว่าการรวบรวมขยะจะถูกเรียกใช้จริง
Simon Forsberg

3
ไม่รับประกันว่าจะมีการรวบรวมประเภทใด มีความเกี่ยวข้องเนื่องจาก Java GCs ส่วนใหญ่เป็นนักสะสม "generational"
สตีเฟ่นซี

5

การสรุปจะพิมพ์จำนวนการสร้างชั้นเรียน

protected void finalize() throws Throwable {
    System.out.println("Run F" );
    if ( checkedOut)
        System.out.println("Error: Checked out");
        System.out.println("Class Create Count: " + classCreate);
}

หลัก

while ( true) {
    Book novel=new Book(true);
    //System.out.println(novel.checkedOut);
    //Runtime.getRuntime().runFinalization();
    novel.checkIn();
    new Book(true);
    //System.runFinalization();
    System.gc();

อย่างที่เห็น. เอาท์เอาท์ต่อไปนี้แสดง gc ที่ถูกเรียกใช้งานครั้งแรกเมื่อจำนวนคลาสคือ 36

C:\javaCode\firstClass>java TerminationCondition
Run F
Error: Checked out
Class Create Count: 36
Run F
Error: Checked out
Class Create Count: 48
Run F

4

เมื่อต้องต่อสู้กับวิธีการขั้นสุดท้ายเมื่อเร็ว ๆ นี้ (เพื่อกำจัดพูลการเชื่อมต่อระหว่างการทดสอบ) ฉันต้องบอกว่า finalizer ไม่มีหลายสิ่ง การใช้ VisualVM เพื่อสังเกตและใช้การอ้างอิงที่ไม่ดีเพื่อติดตามการโต้ตอบจริงฉันพบว่าสิ่งต่อไปนี้เป็นจริงในสภาพแวดล้อม Java 8 (Oracle JDK, Ubuntu 15):

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

ความคิดสุดท้าย

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


2

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

  1. การอ้างอิงทั้งหมดของวัตถุนั้นตั้งค่าเป็น null อย่างชัดเจนเช่น object = null
  2. วัตถุถูกสร้างขึ้นภายในบล็อกและการอ้างอิงไม่อยู่ในขอบเขตเมื่อการควบคุมออกจากบล็อกนั้น
  3. วัตถุหลักถูกตั้งค่าเป็นโมฆะถ้าวัตถุเก็บการอ้างอิงของวัตถุอื่นและเมื่อคุณตั้งค่าการอ้างอิงของวัตถุคอนเทนเนอร์วัตถุลูกหรือวัตถุที่มีอยู่จะมีสิทธิ์โดยอัตโนมัติสำหรับการเก็บขยะ
  4. หากวัตถุมีการอ้างอิงสดผ่าน WeakHashMap วัตถุนั้นจะมีสิทธิ์ได้รับการรวบรวมขยะ

หากมีการใช้finalฟิลด์ของวัตถุซ้ำ ๆ ในระหว่างการคำนวณช้าและจะไม่ใช้วัตถุนั้นหลังจากนั้นวัตถุจะยังคงมีชีวิตอยู่จนกระทั่งรหัสแหล่งสุดท้ายร้องขอเขตข้อมูลหรือ JIT สามารถคัดลอกเขตข้อมูลไปยัง ตัวแปรชั่วคราวแล้วละทิ้งวัตถุก่อนการคำนวณ?
supercat

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

@ โฮลเกอร์: ในกรณีที่ JIT สามารถมองเห็นทุกสิ่งที่เกิดขึ้นกับวัตถุระหว่างการสร้างและการละทิ้งมันฉันไม่เห็นเหตุผลใด ๆ ที่ JIT จะยิงผู้เข้ารอบสุดท้าย คำถามจริงจะเป็นสิ่งที่รหัสต้องทำเพื่อให้แน่ใจว่า finalizer ไม่สามารถดำเนินการภายในวิธีการเฉพาะ ใน. NET มีGC.KeepAlive()ฟังก์ชั่นที่ไม่ทำอะไรเลยนอกจากบังคับให้ GC สมมติว่ามันอาจใช้วัตถุ แต่ฉันรู้ว่าไม่มีฟังก์ชั่นดังกล่าวใน Java เราสามารถใช้volatileตัวแปรเพื่อจุดประสงค์นั้นได้ แต่การใช้ตัวแปรเพียงอย่างเดียวเพื่อจุดประสงค์ดังกล่าวจะดูสิ้นเปลือง
supercat

@supercat: JIT ไม่ได้เริ่มการสรุปมันเพียงแค่จัดเรียงโค้ดเพื่อไม่ให้มีการอ้างอิงถึงแม้ว่ามันอาจเป็นประโยชน์ในการเข้าคิวโดยตรงFinalizerReferenceเพื่อที่จะได้ไม่ต้องใช้วงจร GC เพื่อค้นหาว่าไม่มี การอ้างอิง การซิงโครไนซ์ก็เพียงพอที่จะทำให้แน่ใจว่ามีความสัมพันธ์เกิดขึ้นก่อน ตั้งแต่การสรุปอาจ (จริง ๆ แล้ว) กำลังทำงานในเธรดที่แตกต่างกันมันมักจะจำเป็นอย่างเป็นทางการต่อไป Java 9 กำลังจะเพิ่มReference.reachabilityFence
Holger

@Holger: หาก JIT ปรับการสร้างวัตถุให้เหมาะสมที่สุดสิ่งเดียวที่Finalizeจะถูกเรียกได้ก็คือถ้า JIT สร้างรหัสที่ทำเช่นนั้นโดยตรง โดยทั่วไปจะต้องมีการซิงโครไนซ์รหัสสำหรับวัตถุที่คาดว่าจะใช้ในเธรดเดียวเท่านั้นหรือไม่ หากวัตถุดำเนินการบางอย่างที่จำเป็นต้องยกเลิกก่อนที่มันจะถูกยกเลิก (เช่นการเปิดการเชื่อมต่อซ็อกเก็ตและการใช้ประโยชน์จากทรัพยากรในส่วนอื่น ๆ ) การมี finalizer ปิดการเชื่อมต่อในขณะที่รหัสยังคงใช้ซ็อกเก็ต . มันจะเป็นเรื่องปกติสำหรับรหัสการประสานการใช้งาน ...
SuperCat

2

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


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

2

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

protected void finalize(){
    // This is where the finalization code is entered
}

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


1

ระดับที่เราแทนที่วิธีจบ

public class TestClass {    
    public TestClass() {
        System.out.println("constructor");
    }

    public void display() {
        System.out.println("display");
    }
    @Override
    public void finalize() {
        System.out.println("destructor");
    }
}

โอกาสในการสรุปวิธีการที่ถูกเรียก

public class TestGarbageCollection {
    public static void main(String[] args) {
        while (true) {
            TestClass s = new TestClass();
            s.display();
            System.gc();
        }
    }
}

เมื่อหน่วยความจำเต็มไปด้วยวัตถุการถ่ายโอนข้อมูล gc จะเรียกวิธีการสรุป

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


0

Java อนุญาตให้วัตถุใช้วิธีการที่เรียกว่า finalize () ที่อาจถูกเรียก

finalize () วิธีการได้รับการเรียกว่าถ้าตัวรวบรวมขยะพยายามที่จะรวบรวมวัตถุ

หากตัวรวบรวมขยะไม่ทำงานวิธีจะไม่ถูกเรียกใช้

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

ในทางปฏิบัติคุณไม่น่าจะใช้มันในโครงการจริง

เพียงจำไว้ว่ามันอาจไม่ได้รับการเรียกและแน่นอนว่ามันจะไม่ถูกเรียกสองครั้ง กระบวนการ finalize () สามารถเรียกใช้ศูนย์หรือหนึ่งครั้ง

ในโค้ดต่อไปนี้เมธอด finalize () จะไม่สร้างเอาต์พุตเมื่อเรารันตั้งแต่โปรแกรมออกไปก่อนที่จะต้องรันตัวรวบรวมขยะ

แหล่ง


0

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

ตัวอย่าง:

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


0

เป็นแหลมออกในhttps://wiki.sei.cmu.edu/confluence/display/java/MET12-J.+Do+not+use+finalizers ,

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


-3

ลองเรียกใช้โปรแกรมนี้เพื่อความเข้าใจที่ดีขึ้น

public class FinalizeTest 
{       
    static {
        System.out.println(Runtime.getRuntime().freeMemory());
    }

    public void run() {
        System.out.println("run");
        System.out.println(Runtime.getRuntime().freeMemory());
    }

     protected void finalize() throws Throwable { 
         System.out.println("finalize");
         while(true)
             break;          
     }

     public static void main(String[] args) {
            for (int i = 0 ; i < 500000 ; i++ ) {
                    new FinalizeTest().run();
            }
     }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.