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


12

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

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

  1. ตรวจสอบให้แน่ใจว่าไม่ได้กำลังจะหมด
  2. หากเป็นเช่นนั้นให้เรียกตัวเก็บรวบรวมขยะซึ่งจะทำให้หน่วยความจำว่างมากขึ้น
  3. หากหน่วยความจำบางส่วนถูกปล่อยให้อ้างอิงกับตัวให้คำอธิบายไฟล์ให้ปิดทันที มันรู้ว่าหน่วยความจำเป็นของทรัพยากรเพราะหน่วยความจำที่เชื่อมโยงกับทรัพยากรนั้นได้รับการลงทะเบียนใน 'file descriptor registry' เนื่องจากไม่มีคำศัพท์ที่ดีกว่าเมื่อมันถูกเปิดครั้งแรก
  4. เปิดตัวให้คำอธิบายไฟล์ใหม่คัดลอกลงในหน่วยความจำใหม่ลงทะเบียนตำแหน่งหน่วยความจำนั้นใน 'ตัวให้คำอธิบายไฟล์' และส่งคืนไปยังผู้ใช้

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

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

คำตอบ:


4

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

"ตัวสะสมทรัพยากร" จะสามารถตรวจสอบได้ว่ามีการใช้ตัวจัดการ / ตัวชี้ภายในพื้นที่ที่มีการจัดการหรือไม่ แต่โดยนิยามแล้วไม่ทราบว่าเกิดอะไรขึ้นนอกพื้นที่หน่วยความจำ (และเพื่อทำให้สิ่งเลวร้ายยิ่งขึ้น ข้ามขอบเขตกระบวนการ)

ตัวอย่างการปฏิบัติคือ. NET CLR หนึ่งสามารถใช้ C ++ ปรุงแต่งเพื่อเขียนรหัสซึ่งทำงานกับพื้นที่หน่วยความจำที่มีการจัดการและไม่มีการจัดการ จับพอยน์เตอร์และการอ้างอิงสามารถส่งผ่านระหว่างรหัสที่มีการจัดการและไม่มีการจัดการ รหัสที่ไม่มีการจัดการต้องใช้โครงสร้าง / ประเภทพิเศษเพื่ออนุญาตให้ CLR ติดตามการอ้างอิงที่ทำกับทรัพยากรที่มีการจัดการ แต่นั่นเป็นสิ่งที่ดีที่สุดที่จะทำได้ ไม่สามารถทำเช่นเดียวกันกับที่จับและพอยน์เตอร์และเนื่องจากที่กล่าวว่า Resource Collector จะไม่ทราบว่ามันโอเคที่จะปล่อยหมายเลขอ้างอิงหรือตัวชี้เฉพาะ

แก้ไข: เกี่ยวกับ. NET CLR ฉันไม่ได้มีประสบการณ์กับการพัฒนา C ++ กับแพลตฟอร์ม. NET อาจมีกลไกพิเศษที่ช่วยให้ CLR ติดตามการอ้างอิงไปยังหมายเลขอ้างอิง / ตัวชี้ระหว่างรหัสที่ได้รับการจัดการและไม่มีการจัดการ หากเป็นเช่นนั้น CLR สามารถดูแลอายุการใช้งานของทรัพยากรเหล่านั้นและปล่อยเมื่อมีการเคลียร์การอ้างอิงถึงพวกเขาทั้งหมด (อย่างน้อยในบางสถานการณ์ก็ทำได้) ทั้งสองวิธีแนวปฏิบัติที่ดีที่สุดกำหนดว่าจัดการ (โดยเฉพาะอย่างยิ่งที่ชี้ไปที่ไฟล์) และพอยน์เตอร์ควรได้รับการปล่อยตัวทันทีที่ไม่จำเป็น ตัวรวบรวมทรัพยากรจะไม่ปฏิบัติตามสิ่งนั้นนั่นเป็นอีกสาเหตุหนึ่งที่ไม่มี

แก้ไข 2: มันค่อนข้างเล็กน้อยใน CLR / JVM / VMs-in-general เพื่อเขียนโค้ดบางอย่างเพื่อเพิ่มหมายเลขอ้างอิงเฉพาะถ้ามันถูกใช้ภายในพื้นที่ที่มีการจัดการเท่านั้น ใน. NET จะเป็นสิ่งที่ชอบ:

// This class offends many best practices, but it would do the job.
public class AutoReleaseFileHandle {
    // keeps track of how many instances of this class is in memory
    private static int _toBeReleased = 0;

    // the threshold when a garbage collection should be forced
    private const int MAX_FILES = 100;

    public AutoReleaseFileHandle(FileStream fileStream) {
       // Force garbage collection if max files are reached.
       if (_toBeReleased >= MAX_FILES) {
          GC.Collect();
       }
       // increment counter
       Interlocked.Increment(ref _toBeReleased);
       FileStream = fileStream;
    }

    public FileStream { get; private set; }

    private void ReleaseFileStream(FileStream fs) {
       // decrement counter
       Interlocked.Decrement(ref _toBeReleased);
       FileStream.Close();
       FileStream.Dispose();
       FileStream = null;
    }

    // Close and Dispose the Stream when this class is collected by the GC.
    ~AutoReleaseFileHandle() {
       ReleaseFileStream(FileStream);
    }

    // because it's .NET this class should also implement IDisposable
    // to allow the user to dispose the resources imperatively if s/he wants 
    // to.
    private bool _disposed = false;
    public void Dispose() {
      if (_disposed) {
        return;
      }
      _disposed = true;
      // tells GC to not call the finalizer for this instance.
      GC.SupressFinalizer(this);

      ReleaseFileStream(FileStream);
    }
}

// use it
// for it to work, fs.Dispose() should not be called directly,
var fs = File.Open("path/to/file"); 
var autoRelease = new AutoReleaseFileHandle(fs);

3

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

มีบทความดีๆเกี่ยวกับการใช้ finalizers ที่นี่:

การสรุปวัตถุและการล้างข้อมูล

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


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

ตกลง แต่ตัวอธิบายไฟล์มักจะเป็นตัวแทนของ Open File ในระบบปฏิบัติการซึ่งหมายถึง (ขึ้นอยู่กับระบบปฏิบัติการ) ใช้ทรัพยากรระดับระบบเช่นล็อคพูลพูลบัฟเฟอร์โครงสร้างพูล ฯลฯ ตรงไปตรงมาฉันไม่เห็นประโยชน์ของการปล่อยให้โครงสร้างเหล่านี้เปิดสำหรับการเก็บขยะในภายหลังและฉันเห็นผู้ร้ายจำนวนมากปล่อยให้พวกเขาจัดสรรนานเกินความจำเป็น กระบวนการ Finalize () มีจุดประสงค์เพื่ออนุญาตให้มีการล้างข้อมูลครั้งสุดท้ายในกรณีที่โปรแกรมเมอร์มองข้ามสายเพื่อล้างทรัพยากร แต่ไม่ควรเชื่อถือ
Brian Hibbert

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

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

3

มีเทคนิคการเขียนโปรแกรมมากมายที่จะช่วยจัดการทรัพยากรประเภทนี้

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

  • Java, C #, และภาษาอื่น ๆ อีกมากมายให้วิธีการระบุวิธีการที่จะเรียกเมื่อวัตถุไม่ได้อยู่อีกต่อไปและจะถูกรวบรวมโดยขยะเก็บ ดูเช่นผู้เข้ารอบสุดท้ายdispose()และอื่น ๆ ความคิดคือโปรแกรมเมอร์สามารถใช้วิธีการดังกล่าวเพื่อที่จะปิดทรัพยากรอย่างชัดเจนก่อนที่วัตถุจะเป็นอิสระจากตัวเก็บขยะ อย่างไรก็ตามวิธีการเหล่านี้มีปัญหาบางอย่างซึ่งคุณสามารถอ่านเกี่ยวกับที่อื่น; ตัวอย่างเช่นตัวรวบรวมขยะอาจไม่รวบรวมวัตถุจนกว่าจะช้ากว่าที่คุณต้องการ

  • C # และภาษาอื่น ๆ มีusingคำหลักที่ช่วยให้มั่นใจว่าทรัพยากรจะปิดหลังจากที่พวกเขาไม่ต้องการอีกต่อไป (ดังนั้นคุณอย่าลืมปิดไฟล์ descriptor หรือทรัพยากรอื่น ๆ ) สิ่งนี้มักจะดีกว่าการพึ่งพาตัวรวบรวมขยะเพื่อค้นหาว่าวัตถุนั้นไม่ได้อยู่อีกต่อไป ดูเช่นhttps://stackoverflow.com/q/75401/781723 คำทั่วไปที่นี่เป็นทรัพยากรที่มีการจัดการ ความคิดนี้สร้างบน RAII และ finalizers ปรับปรุงในบางวิธี


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

2

หน่วยความจำทั้งหมดเท่ากันถ้าฉันขอ 1K ฉันไม่สนใจว่าในพื้นที่ที่อยู่ที่มาจาก 1K

เมื่อฉันขอหมายเลขอ้างอิงไฟล์ฉันต้องการหมายเลขอ้างอิงไปยังไฟล์ที่ฉันต้องการเปิด การเปิดไฟล์จัดการกับไฟล์มักจะบล็อกการเข้าถึงไฟล์โดยกระบวนการอื่นหรือเครื่อง

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

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


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

0

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

หมายเหตุแน่นอนตัวอย่างของคุณสามารถให้ได้ในตอนนี้โดยใช้ตัวอธิบายไฟล์ "อ่อน" ด้วยเทคนิค GC ที่มีอยู่


0

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

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