สวัสดี!
ฉันคิดว่าคุณคงไม่แปลกใจถ้าฉันบอกคุณว่าคอมพิวเตอร์ของคุณมีหน่วยความจำจำกัด :) แม้แต่ฮาร์ดไดรฟ์ — โดยทั่วไปจะมีขนาดใหญ่กว่าหน่วยความจำ RAM หลายเท่า — ก็สามารถบรรจุเกม รายการทีวี รายการโปรดของคุณได้เต็มความจุ และอื่น ๆ. เพื่อป้องกันไม่ให้สิ่งนี้เกิดขึ้น คุณต้องตรวจสอบสถานะปัจจุบันของหน่วยความจำและลบไฟล์ที่ไม่จำเป็นออกจากคอมพิวเตอร์ของคุณ การเขียนโปรแกรม Java เกี่ยวข้องกับสิ่งเหล่านี้อย่างไร ทุกอย่าง! ท้ายที่สุดแล้ว เมื่อเครื่อง Java สร้างวัตถุใด ๆ ก็จะจัดสรรหน่วยความจำสำหรับวัตถุนั้น
ในโปรแกรมขนาดใหญ่จริงๆ มีการสร้างอ็อบเจกต์นับหมื่นนับแสนอ็อบเจกต์ และแต่ละออบเจกต์มีหน่วยความจำของตัวเองจัดสรรไว้ให้ แต่คุณคิดว่าวัตถุเหล่านี้มีอยู่นานแค่ไหน? พวกเขา "มีชีวิตอยู่" ตลอดเวลาที่โปรแกรมของเราทำงานหรือไม่? ไม่แน่นอน แม้ว่าจะมีข้อดีทั้งหมดของออบเจกต์ Java แต่ก็ไม่ได้เป็นอมตะ :) ออบเจ็กต์มีวงจรชีวิตของมันเอง วันนี้เราจะหยุดพักจากการเขียนโค้ดสักหน่อยแล้วมาดูกระบวนการนี้กัน :) ยิ่งไปกว่านั้น ความเข้าใจของคุณเกี่ยวกับวิธีการทำงานของโปรแกรมและวิธีการจัดการทรัพยากรเป็นสิ่งสำคัญมาก ชีวิตของวัตถุเริ่มต้นเมื่อใด เหมือนคน - ตั้งแต่กำเนิดนั่นคือการสร้าง
Cat cat = new Cat(); // Here the lifecycle of our Cat object begins!
ขั้นแรก Java Virtual Machine จะจัดสรรหน่วยความจำตามจำนวนที่ต้องการเพื่อสร้างอ็อบเจกต์ จากนั้นจะสร้างการอ้างอิงถึงหน่วยความจำนั้น ในกรณีของเรา การอ้างอิงนั้นเรียกว่าcat
ดังนั้นเราจึงสามารถติดตามได้ จากนั้นตัวแปรทั้งหมดจะถูกเตรียมใช้งาน ตัวสร้างจะถูกเรียก และ — ta-da! — วัตถุที่เพิ่งสร้างเสร็จของเรากำลังมีชีวิตของมันเอง :)
อายุการใช้งานของวัตถุแตกต่างกันไป เราจึงไม่สามารถระบุตัวเลขที่แน่นอนได้ที่นี่ ไม่ว่าในกรณีใด ๆ มันอาศัยอยู่ในโปรแกรมและทำหน้าที่ของมันเป็นระยะเวลาหนึ่ง เพื่อให้แม่นยำ วัตถุจะ "มีชีวิต" ตราบใดที่มีการอ้างอิงถึงวัตถุนั้น ทันทีที่ไม่มีการอ้างอิงเหลือ วัตถุ "ตาย" ตัวอย่าง:
public class Car {
String model;
public Car(String model) {
this.model = model;
}
public static void main(String[] args) {
Car lamborghini = new Car("Lamborghini Diablo");
lamborghini = null;
}
}
วัตถุรถ Lamborghini Diablo หยุดมีชีวิตในบรรทัดที่สองของmain()
วิธีการ มีการอ้างอิงเพียงรายการเดียว จากนั้นการอ้างอิงนั้นถูกกำหนดnull
เท่ากับ เนื่องจากไม่มีการอ้างอิงถึง Lamborghini Diablo เหลืออยู่ หน่วยความจำที่จัดสรรจึงกลายเป็น "ขยะ" การอ้างอิงไม่จำเป็นต้องตั้งค่าเป็นโมฆะเพื่อให้สิ่งนี้เกิดขึ้น:
public class Car {
String model;
public Car(String model) {
this.model = model;
}
public static void main(String[] args) {
Car lamborghini = new Car("Lamborghini Diablo");
Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
lamborghini = lamborghiniGallardo;
}
}
ที่นี่เราสร้างวัตถุที่สองแล้วกำหนดวัตถุใหม่นี้ให้กับการlamborghini
อ้างอิง ตอนนี้Lamborghini Gallardo
วัตถุมีการอ้างอิงสองรายการ แต่Lamborghini Diablo
วัตถุไม่มี นั่นหมายความว่าDiablo
ตอนนี้วัตถุนั้นเป็นขยะ นี่คือเมื่อกลไกในตัวของ Java ที่เรียกว่าตัวรวบรวมขยะ (GC) เข้ามามีบทบาท
ตัวรวบรวมขยะเป็นกลไก Java ภายในที่รับผิดชอบในการเพิ่มหน่วยความจำ เช่น การลบวัตถุที่ไม่จำเป็นออกจากหน่วยความจำ มีเหตุผลที่ดีว่าทำไมเราเลือกรูปภาพหุ่นยนต์ดูดฝุ่นที่นี่ ท้ายที่สุดแล้วตัวเก็บขยะก็ทำงานในลักษณะเดียวกัน: ในพื้นหลัง มันจะ "เดินทาง" ไปตามโปรแกรมของคุณ รวบรวมขยะโดยที่คุณแทบไม่ต้องใช้ความพยายามใดๆ เลย หน้าที่ของมันคือลบวัตถุที่ไม่ได้ใช้ในโปรแกรมแล้ว
การทำเช่นนี้จะทำให้หน่วยความจำในคอมพิวเตอร์ว่างสำหรับวัตถุอื่นๆ คุณจำได้ไหมว่าในตอนต้นของบทเรียนเรากล่าวว่าในชีวิตปกติคุณต้องตรวจสอบสถานะของคอมพิวเตอร์และลบไฟล์ที่ไม่จำเป็น? ในกรณีของวัตถุ Java ตัวรวบรวมขยะจะทำสิ่งนี้ให้คุณ ตัวรวบรวมขยะจะทำงานซ้ำๆ ขณะที่โปรแกรมของคุณทำงาน: คุณไม่จำเป็นต้องเรียกหรือออกคำสั่งอย่างชัดเจน แม้ว่าจะเป็นไปได้ทางเทคนิคก็ตาม ในภายหลังเราจะพูดถึงเรื่องนี้มากขึ้นและวิเคราะห์งานโดยละเอียดยิ่งขึ้น
เมื่อตัวรวบรวมขยะไปถึงวัตถุ ก่อนที่จะทำลายวัตถุนั้น จะเรียกวิธีการพิเศษ — finalize()
— กับวัตถุนั้น วิธีนี้สามารถปล่อยทรัพยากรอื่นที่ใช้โดยวัตถุ วิธีfinalize()
การเป็นส่วนหนึ่งของObject
ชั้นเรียน ซึ่งหมายความว่านอกเหนือจากequals()
, hashCode()
และtoString()
เมธอดที่คุณพบก่อนหน้านี้แล้ว ทุกออบเจกต์มีเมธอดนี้ มันแตกต่างจากวิธีการอื่นตรงที่ว่า - ฉันจะใส่สิ่งนี้ได้อย่างไร - ตามอำเภอใจมาก
โดยเฉพาะอย่างยิ่งมันไม่ได้ถูกเรียกก่อนที่จะทำลายวัตถุเสมอไป การเขียนโปรแกรมเป็นความพยายามที่แม่นยำ โปรแกรมเมอร์บอกให้คอมพิวเตอร์ทำบางอย่าง และคอมพิวเตอร์ก็ทำตามนั้น ฉันคิดว่าคุณคุ้นเคยกับพฤติกรรมนี้แล้ว ดังนั้นในตอนแรกอาจเป็นเรื่องยากสำหรับคุณที่จะยอมรับแนวคิดต่อไปนี้: "ก่อนการทำลายวัตถุ วิธีการของคลาสจะถูกเรียก หรืออาจจะไม่ถูกเรียก ทั้งหมดขึ้นfinalize()
อยู่Object
กับ โชคของคุณ!"
แต่มันเป็นความจริง เครื่อง Java เองจะกำหนดว่าจะเรียกfinalize()
เมธอดเป็นกรณีไป หรือไม่ ตัวอย่างเช่น ลองรันโค้ดต่อไปนี้เป็นการทดสอบ:
public class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public Cat() {
}
public static void main(String[] args) throws Throwable {
for (int i = 0 ; i < 1000000; i++) {
Cat cat = new Cat();
cat = null; // This is when the first object becomes available to the garbage collector
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("Cat object destroyed!");
}
}
เราสร้างCat
วัตถุ และในโค้ดบรรทัดถัดไป เราตั้งค่าการอ้างอิงเดียวของวัตถุนั้นให้เป็นค่าว่าง และเราทำอย่างนั้นเป็นล้านครั้ง เราลบล้างfinalize()
เมธอดอย่างชัดเจนเพื่อให้พิมพ์สตริงไปยังคอนโซลเป็นล้านครั้ง (หนึ่งครั้งต่อการทำลายอ็Cat
อบเจกต์แต่ละครั้ง) แต่ไม่มี! พูดให้ชัดคือมันรันบนคอมพิวเตอร์ของฉันเพียง 37,346 ครั้งเท่านั้น! นั่นคือเพียงครั้งเดียวใน 27 ครั้งที่เครื่อง Java ที่ติดตั้งในเครื่องของฉันตัดสินใจเรียกfinalize()
เมธอด
ในกรณีอื่น ๆ การรวบรวมขยะเกิดขึ้นโดยไม่มีมัน ลองรันโค้ดนี้ด้วยตัวคุณเอง ส่วนใหญ่แล้วคุณจะได้ผลลัพธ์ที่แตกต่างออกไป อย่างที่คุณเห็นfinalize()
แทบจะไม่สามารถเรียกได้ว่าเป็นพันธมิตรที่เชื่อถือได้ :) ดังนั้น คำแนะนำเล็กน้อยสำหรับอนาคต: อย่าพึ่งพาวิธีfinalize()
การในการทำให้ทรัพยากรสำคัญฟรี บางที JVM จะเรียกมันหรืออาจจะไม่ ใครจะรู้?
หากในขณะที่อ็อบเจ็กต์ของคุณยังมีชีวิตอยู่ ทรัพยากรบางอย่างที่มีความสำคัญอย่างยิ่งต่อประสิทธิภาพการทำงาน เช่น การเชื่อมต่อฐานข้อมูลแบบเปิด จะเป็นการดีกว่าหากสร้างเมธอดพิเศษในคลาสของคุณเพื่อรีลีสแล้วเรียกใช้อย่างชัดเจนเมื่ออ็อบเจ็กต์ไม่อยู่แล้ว จำเป็น ด้วยวิธีนี้คุณจะรู้ได้อย่างแน่นอนว่าประสิทธิภาพของโปรแกรมของคุณจะไม่ได้รับผลกระทบ ตั้งแต่เริ่มแรก เรากล่าวว่าการทำงานกับหน่วยความจำและการกำจัดขยะนั้นสำคัญมาก และนี่คือความจริง การจัดการทรัพยากรอย่างไม่เหมาะสมและความเข้าใจผิดว่าออบเจกต์ที่ไม่จำเป็นได้รับการล้างข้อมูลสามารถนำไปสู่การรั่วไหลของหน่วยความจำได้ นี่เป็นหนึ่งในข้อผิดพลาดในการเขียนโปรแกรมที่เป็นที่รู้จักมากที่สุด
หากโปรแกรมเมอร์เขียนโค้ดไม่ถูกต้อง หน่วยความจำใหม่อาจถูกจัดสรรให้กับอ็อบเจกต์ที่สร้างขึ้นใหม่ในแต่ละครั้ง ในขณะที่อ็อบเจกต์เก่าที่ไม่จำเป็นอาจไม่สามารถลบออกได้โดยตัวเก็บขยะ เนื่องจากเราได้เปรียบเทียบหุ่นยนต์ดูดฝุ่น ลองจินตนาการว่าจะเกิดอะไรขึ้นถ้าคุณทำถุงเท้ากระจายก่อนเริ่มหุ่นยนต์ ทำแจกันแก้วแตก และทิ้งตัวต่อเลโก้ไว้เต็มพื้น แน่นอนว่าหุ่นยนต์จะพยายามทำงานของมัน แต่เมื่อถึงจุดหนึ่งมันก็จะติดขัด
เพื่อให้หุ่นยนต์ดูดฝุ่นทำงานได้อย่างถูกต้อง คุณต้องรักษาพื้นให้อยู่ในสภาพดีและนำสิ่งที่หุ่นยนต์ไม่สามารถจัดการออกได้ หลักการเดียวกันนี้ใช้กับตัวรวบรวมขยะของ Java หากมีวัตถุจำนวนมากเหลืออยู่ในโปรแกรมที่ไม่สามารถทำความสะอาดได้ (เช่น ถุงเท้าหรือตัวต่อเลโก้สำหรับหุ่นยนต์ดูดฝุ่นของเรา) เมื่อถึงจุดหนึ่งหน่วยความจำของคุณจะหมด และอาจไม่ใช่แค่โปรแกรมของคุณเท่านั้นที่จะค้าง — ทุกโปรแกรมที่ทำงานบนคอมพิวเตอร์อาจได้รับผลกระทบ พวกเขาอาจมีหน่วยความจำไม่เพียงพอ
คุณไม่จำเป็นต้องท่องจำสิ่งนี้ คุณเพียงแค่ต้องเข้าใจหลักการเบื้องหลังวิธีการทำงาน
GO TO FULL VERSION