การตั้งค่าออบเจ็กต์ Java เป็น null จะทำอะไรอีกต่อไปหรือไม่?


112

ฉันกำลังดูหนังสือเก่า ๆ และพบสำเนา "Practical Java" ของ Peter Hagger ในส่วนประสิทธิภาพมีคำแนะนำในการตั้งค่าการอ้างอิงอ็อบเจ็กต์nullเมื่อไม่จำเป็นอีกต่อไป

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

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


2
โปรไฟล์มัน ในช่วงเวลาที่ทันสมัยคุณไม่ควรเห็นประสิทธิภาพที่เพิ่มขึ้นอย่างมีนัยสำคัญหรือรอยเท้าหน่วยความจำ
Jason Coco

12
@ เจสันโปรไฟล์มัน? นั่นถือว่าฉันจะกำหนดรายละเอียดกรณีที่ใหญ่พอที่จะได้ผลลัพธ์ที่ดีพอที่จะตอบคำถามนี้ และฉันไม่ได้เลือกชุดเคสที่ VM ได้รับการปรับให้เหมาะสมเพียงพอที่จะปกปิดปัญหา gc และประสิทธิภาพ นั่นเป็นเหตุผลที่ฉันถามที่นี่ เพื่อให้ทราบถึงกรณีที่เป็นปัญหา
พะยอม

คำตอบ:


73

ขึ้นอยู่กับว่าคุณคิดจะลบข้อมูลอ้างอิงเมื่อใด

หากคุณมีห่วงโซ่ออบเจ็กต์ A-> B-> C เมื่อ A ไม่สามารถเข้าถึงได้ A, B และ C ทั้งหมดจะมีสิทธิ์ได้รับการเก็บขยะ (สมมติว่าไม่มีสิ่งอื่นใดที่อ้างอิงถึง B หรือ C) ไม่จำเป็นและไม่จำเป็นต้องตั้งค่าการอ้างอิงอย่างชัดเจน A-> B หรือ B-> C เป็น null เป็นต้น

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

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

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;             <-- explicitly set to null
  doSomethingElse();
}

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


2
เครื่องสามารถปรับตัวแปรโลคัลได้ ตัวแปรคลาสไม่สามารถ ฉันเคยเห็นเธรดของผู้ปฏิบัติงาน (บนเธรดพูล) ไม่ปล่อยอ็อบเจ็กต์สถานะของพวกเขาหลังจากจัดการกับคำร้องขอแล้วจึงตรึงอ็อบเจ็กต์เหล่านั้นไว้ในหน่วยความจำจนกว่าเธรดผู้ปฏิบัติงานจะได้รับงานใหม่ (ซึ่งจะเขียนทับอ็อบเจ็กต์สถานะเก่าด้วยอ็อบเจ็กต์ใหม่ทันที) ด้วยออบเจ็กต์สถานะขนาดใหญ่และเธรดผู้ปฏิบัติงานหลายร้อยรายการ (คิดว่า: เซิร์ฟเวอร์ http ขนาดใหญ่) อาจเป็นหน่วยความจำจำนวนมหาศาลที่เก็บไว้และคัดลอกไปรอบ ๆ แต่จะไม่ถูกนำมาใช้อีก
Brian White

1
ฉันควรจะเพิ่ม: คำแนะนำที่ฉันให้ไว้ข้างต้นเป็นคำแนะนำที่ต้องปฏิบัติตามโดยทั่วไปสมมติว่าไลบรารีที่มีพฤติกรรมดี VM ที่มีพฤติกรรมดีเป็นต้นหากคุณพบว่าคุณมีไลบรารีพูลเธรดที่แขวนอยู่กับวัตถุหลังจาก งานเสร็จแล้ว ... นั่นเป็นข้อผิดพลาดในไลบรารีเธรดพูลที่กล่าวว่าอาจสามารถแก้ไขได้โดยการลบการอ้างอิงอ็อบเจ็กต์ แต่อาจใช้งานได้โดยใช้ไลบรารีพูลเธรดที่มีบั๊กน้อยกว่า ฉันไม่แน่ใจว่าจุดบกพร่องดังกล่าวเปลี่ยนแปลงหลักการออกแบบทั่วไป
Neil Coffey

25

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

ดู Effective Java 2nd ed, Item 6: กำจัดการอ้างอิงอ็อบเจ็กต์ที่ล้าสมัย


นี่เป็นปัญหาด้านภาษาหรือปัญหาการใช้งาน VM หรือไม่
Pablo Santa Cruz

4
มันเป็นปัญหาด้าน "ความหมาย" โดยพื้นฐานแล้วเนื่องจากคุณได้จัดสรรอาร์เรย์ไว้ล่วงหน้าแล้ว VM จึงเห็นสิ่งนั้น มันไม่รู้อะไรเลยเกี่ยวกับขนาด "ตรรกะ" ของคอนเทนเนอร์ของคุณ สมมติว่าคุณมี ArrayList ขนาด 10 ซึ่งสนับสนุนโดยอาร์เรย์ 16 องค์ประกอบ VM ไม่สามารถรู้ได้ว่ารายการ 10..15 ไม่ได้ใช้จริง หากช่องเหล่านั้นมีเนื้อหาก็จะไม่ถูกปล่อยให้เป็นอิสระ
Chris Jester-Young

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

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

2
ในรายการ 6 เดียวกันที่คุณกล่าวถึง: การยกเลิกการอ้างอิงอ็อบเจ็กต์ควรเป็นข้อยกเว้นแทนที่จะเป็นบรรทัดฐาน
ACV

10

ช่องอินสแตนซ์องค์ประกอบอาร์เรย์

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

กรณีทางพยาธิวิทยาเป็นอ็อบเจ็กต์ที่เก็บอินสแตนซ์ unnessary ไว้กับแผนผัง XML DOM ทั้งหมดที่ใช้ในการกำหนดค่า MBean ที่ไม่ได้ยกเลิกการลงทะเบียนหรือการอ้างอิงเดียวไปยังอ็อบเจ็กต์จากเว็บแอ็พพลิเคชันที่ไม่ได้ปรับใช้ซึ่งป้องกันไม่ให้โหลดคลาสทั้งหมดไม่โหลด .

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

ตัวแปรที่กำหนดขอบเขต:

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

{
  BigObject obj = ...
  doSomethingWith(obj);
  obj = null;          //   <-- explicitly set to null
  doSomethingElse();
}

กลายเป็น

{
  {  
     BigObject obj = ...
     doSomethingWith(obj);
  }    //         <-- obj goes out of scope
  doSomethingElse();
}

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


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

5

ในสภาพแวดล้อมที่ จำกัด หน่วยความจำ (เช่นโทรศัพท์มือถือ) สิ่งนี้จะมีประโยชน์ ด้วยการตั้งค่า null objetc ไม่จำเป็นต้องรอให้ตัวแปรออกจากขอบเขตเพื่อเป็น gc

อย่างไรก็ตามสำหรับการเขียนโปรแกรมในชีวิตประจำวันสิ่งนี้ไม่ควรเป็นกฎยกเว้นในกรณีพิเศษเช่นที่ Chris Jester-Young อ้างถึง


1

nullประการแรกก็ไม่ได้หมายความว่าสิ่งที่คุณกำลังตั้งวัตถุให้ ฉันอธิบายไว้ด้านล่าง:

List list1 = new ArrayList();
List list2 = list1;

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

list1 = null;

นั่นหมายความว่าlist1จะไม่อ้างถึงวัตถุใด ๆ ที่เก็บไว้ในหน่วยความจำอีกต่อไปดังนั้นlist2จะไม่มีอะไรให้อ้างอิงอีกต่อไป ดังนั้นหากคุณตรวจสอบขนาดของlist2:

list2.size(); //it gives you 0

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

ฉันหวังว่ามันจะชัดเจนแนวคิด


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