ความจำรั่วไหลไปได้ไกลแค่ไหน?


118

ฉันเจอปัญหาหน่วยความจำรั่วหลายครั้ง โดยปกติเมื่อฉันรู้สึกmallocเหมือนไม่มีวันพรุ่งนี้หรือห้อยโหนFILE *เหมือนซักผ้าสกปรก โดยทั่วไปฉันถือว่า (อ่าน: หวังเป็นอย่างยิ่ง) ว่าหน่วยความจำทั้งหมดจะถูกล้างอย่างน้อยที่สุดเมื่อโปรแกรมสิ้นสุดลง มีสถานการณ์ใดบ้างที่จะไม่รวบรวมหน่วยความจำที่รั่วไหลเมื่อโปรแกรมหยุดทำงานหรือล่ม?

หากคำตอบแตกต่างกันอย่างมากในแต่ละภาษาให้เน้นที่ C (++)

โปรดสังเกตการใช้วลีที่เกินความจริงเช่น 'เหมือนไม่มีวันพรุ่งนี้' และ 'ห้อย ... เหมือนซักผ้าสกปรก' ความไม่ปลอดภัย * malloc* อาจทำร้ายคนที่คุณรักได้ นอกจากนี้โปรดใช้ความระมัดระวังในการซักผ้าที่สกปรก


3
หากคุณใช้ระบบปฏิบัติการที่ "ทันสมัย" เช่น Linux หรือ Windows ระบบปฏิบัติการจะแก้ไขหน่วยความจำที่ยังไม่ได้เผยแพร่เมื่อโปรแกรมหยุดทำงาน
Oliver Charlesworth

60
แทนที่จะเป็น malloc-ing เหมือนไม่มีวันพรุ่งนี้ลองแกล้งทำเป็นว่ามีพรุ่งนี้และติดตามความทรงจำของคุณ!
William Pursell

8
@WilliamPursell อ่าคุณกำลังบอกว่าcallocไม่มีวันพรุ่งนี้ ยอดเยี่ยม
DilithiumMatrix

8
"หากคำตอบแตกต่างกันอย่างมากในแต่ละภาษาให้เน้นที่ c (++)" cและc ++ไม่ใช่ภาษาเดียวกัน!
Johnsyweb

11
@zhermes: ความคิดเห็นเกี่ยวกับ C และ C ++ เป็นภาษาที่แตกต่างกันซ่อนอยู่มากกว่าที่คุณคิด ... ใน C ++ คุณจะพบว่าตัวเองใช้ประโยชน์จากวัตถุที่มีระยะเวลาการจัดเก็บอัตโนมัติทำตามสำนวน RAII ... คุณปล่อยให้วัตถุเหล่านี้ดูแลหน่วยความจำ การจัดการสำหรับคุณ
LihO

คำตอบ:


111

ไม่ระบบปฏิบัติการปลดปล่อยทรัพยากรทั้งหมดที่มีอยู่ในกระบวนการเมื่อออกจากระบบ

สิ่งนี้ใช้กับทรัพยากรทั้งหมดที่ระบบปฏิบัติการดูแล: หน่วยความจำ, ไฟล์ที่เปิด, การเชื่อมต่อเครือข่าย, ที่จับหน้าต่าง ...

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

ระบบปฏิบัติการอาจใช้เวลานานในการปลดปล่อยทรัพยากรบางอย่าง ตัวอย่างเช่นพอร์ต TCP ที่เซิร์ฟเวอร์เครือข่ายใช้เพื่อยอมรับการเชื่อมต่ออาจใช้เวลาหลายนาทีกว่าจะว่างแม้ว่าโปรแกรมจะปิดอย่างถูกต้องก็ตาม โปรแกรมบนเครือข่ายอาจเก็บทรัพยากรระยะไกลเช่นวัตถุฐานข้อมูล ระบบระยะไกลควรปลดปล่อยทรัพยากรเหล่านั้นเมื่อขาดการเชื่อมต่อเครือข่าย แต่อาจใช้เวลานานกว่าระบบปฏิบัติการภายใน


5
กระบวนทัศน์ทั่วไปใน RTOSs คือกระบวนการเดียวแบบจำลองเธรดหลายชุดและไม่มีการป้องกันหน่วยความจำระหว่าง 'งาน' โดยปกติจะมีหนึ่งกอง นี่เป็นวิธีที่ VxWorks ใช้ในการทำงาน - และอาจจะยังคงทำอยู่
marko

29
โปรดทราบว่าระบบปฏิบัติการไม่สามารถปลดปล่อยทรัพยากรทั้งหมดได้ การเชื่อมต่อเครือข่ายธุรกรรมฐานข้อมูล ฯลฯ การไม่ปิดอย่างชัดเจนอาจทำให้เกิดผลลัพธ์ที่ไม่พึงปรารถนา การไม่ปิดการเชื่อมต่อเครือข่ายอาจทำให้เซิร์ฟเวอร์คิดว่าคุณยังคงใช้งานอยู่เป็นระยะเวลาไม่ จำกัด และสำหรับเซิร์ฟเวอร์ที่ จำกัด จำนวนการเชื่อมต่อที่ใช้งานอยู่อาจทำให้เกิดการปฏิเสธการให้บริการโดยไม่ได้ตั้งใจ การไม่ปิดธุรกรรมฐานข้อมูลอาจทำให้คุณสูญเสียข้อมูลที่ไม่ได้ผูกมัด
Lie Ryan

1
@Marko: vxWorks เวอร์ชันล่าสุดรองรับ RTP แล้ว (กระบวนการตามเวลาจริง) ซึ่งรองรับการป้องกันหน่วยความจำ
Xavier T.

20
"ระบบปฏิบัติการปลดปล่อยทรัพยากรทั้งหมดที่มีโดยกระบวนการเมื่อออกจากระบบ" ไม่จริงอย่างเคร่งครัด ตัวอย่างเช่นบน (อย่างน้อย) Linux, SysV semaphores และอ็อบเจ็กต์ IPC อื่น ๆ จะไม่ถูกล้างข้อมูลเมื่อออกจากกระบวนการ นั่นเป็นเหตุผลที่มีipcrmคู่มือการทำความสะอาดlinux.die.net/man/8/ipcrm
sleske

7
นอกจากนี้ถ้าวัตถุมีไฟล์ชั่วคราวที่จะรักษาที่เห็นได้ชัดว่าจะไม่ได้รับการทำความสะอาดขึ้นหลังจากนั้น
หมูปิ้ง

47

มาตรฐาน C ไม่ได้ระบุว่าหน่วยความจำที่จัดสรรโดยmallocถูกปล่อยออกมาเมื่อโปรแกรมสิ้นสุดลง สิ่งนี้ทำได้โดยระบบปฏิบัติการไม่ใช่ OS ทั้งหมด (โดยปกติจะอยู่ในโลกฝังตัว) จะปล่อยหน่วยความจำเมื่อโปรแกรมสิ้นสุด


20
ไม่มากก็น้อยเพราะมาตรฐาน C พูดถึงโปรแกรม C ไม่ใช่ระบบปฏิบัติการที่ C เกิดขึ้นเพื่อเรียกใช้ ...
vonbrand

5
@vonbrand มาตรฐาน C อาจมีย่อหน้าที่ระบุว่าเมื่อmainส่งคืนหน่วยความจำทั้งหมดที่จัดสรรโดยmallocจะถูกปล่อยออกมา ตัวอย่างเช่นบอกว่าไฟล์ที่เปิดอยู่ทั้งหมดจะถูกปิดก่อนที่โปรแกรมจะหยุดทำงาน สำหรับหน่วยความจำที่จัดสรรของฉันmallocมันไม่ได้ระบุไว้เท่านั้น แน่นอนว่าประโยคของฉันเกี่ยวกับ OS อธิบายถึงสิ่งที่มักจะทำไม่ใช่สิ่งที่มาตรฐานกำหนดเนื่องจากไม่ได้ระบุอะไรในเรื่องนี้
ouah

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

2
@ouah: " เมื่อผลตอบแทนหลัก ... ". นั่นเป็นข้อสันนิษฐาน เราต้องพิจารณา " ถ้าผลตอบแทนหลัก ... " std::atexitยังพิจารณายกเลิกโปรแกรมผ่านทางstd::exitแล้วนอกจากนี้ยังมีstd::abortและ (C ++ std::terminateเฉพาะ)
MSalters

@ouah: หากรวมไว้แล้วatexitจะไม่สามารถใช้งานได้ :-)
R .. GitHub STOP HELPING ICE

28

เนื่องจากคำตอบทั้งหมดได้ครอบคลุมประเด็นส่วนใหญ่ของคำถามของคุณคือ OSes สมัยใหม่ แต่ในอดีตมีสิ่งหนึ่งที่ควรค่าแก่การกล่าวถึงหากคุณเคยตั้งโปรแกรมในโลก DOS โดยทั่วไปโปรแกรม Terminant และ Stay Resident (TSR) จะส่งคืนการควบคุมไปยังระบบ แต่จะอยู่ในหน่วยความจำซึ่งอาจได้รับการฟื้นฟูโดยซอฟต์แวร์ / ฮาร์ดแวร์ขัดจังหวะ เป็นเรื่องปกติที่จะเห็นข้อความเช่น"หน่วยความจำไม่เพียงพอลองยกเลิกการโหลด TSR บางส่วนของคุณ"เมื่อทำงานกับระบบปฏิบัติการเหล่านี้

ดังนั้นในทางเทคนิคโปรแกรมจะหยุดทำงาน แต่เนื่องจากยังคงอยู่ในหน่วยความจำการรั่วไหลของหน่วยความจำใด ๆ จะไม่ถูกปล่อยออกมาเว้นแต่คุณจะยกเลิกการโหลดโปรแกรม

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

ฉันจำได้อีกหนึ่งตัวอย่าง Customer Information Control System (CICS) เซิร์ฟเวอร์ธุรกรรมที่ทำงานบนเมนเฟรมของ IBM เป็นหลักเป็นการสนทนาหลอก เมื่อดำเนินการจะประมวลผลข้อมูลที่ผู้ใช้ป้อนสร้างชุดข้อมูลอื่นสำหรับผู้ใช้โอนไปยังโหนดปลายทางของผู้ใช้และยุติ ในการเปิดใช้งานคีย์ความสนใจจะฟื้นขึ้นมาอีกครั้งเพื่อประมวลผลข้อมูลชุดอื่น เนื่องจากวิธีการทำงานในทางเทคนิคอีกครั้งระบบปฏิบัติการจะไม่เรียกคืนหน่วยความจำจากโปรแกรม CICS ที่ถูกยกเลิกเว้นแต่คุณจะรีไซเคิลเซิร์ฟเวอร์ธุรกรรม CICS


มันน่าสนใจมากขอบคุณสำหรับบันทึกประวัติศาสตร์! คุณรู้หรือไม่ว่ากระบวนทัศน์นั้นเกิดจากการปลดปล่อยหน่วยความจำซึ่งมีค่าใช้จ่ายสูงเกินไปหากไม่จำเป็น? หรือมีทางเลือกอื่นที่ยังไม่เคยคิด?
DilithiumMatrix

1
@zhermes: มันเป็นไปไม่ได้ในการคำนวณเนื่องจาก DOS ไม่ได้ติดตามการจัดสรรหน่วยความจำสำหรับ TSR สวยมากโดยความหมาย: เป้าหมายคือการอยู่อาศัย หากคุณต้องการให้ TSR ของคุณปลดปล่อยหน่วยความจำบางส่วน แต่ไม่ใช่ทั้งหมดคุณต้องตัดสินใจว่าจะให้อะไรฟรี
MSalters

2
@zhermes: DOS (เช่น CP / M บรรพบุรุษของมัน) ไม่ใช่สิ่งที่คุณเรียกว่าระบบปฏิบัติการในความหมายสมัยใหม่ มันเป็นเพียงชุดของยูทิลิตี้ I / O ที่สามารถเรียกได้ตามวิธีมาตรฐานที่มาพร้อมกับโปรเซสเซอร์คำสั่งที่จะให้คุณรันทีละโปรแกรม ไม่มีความคิดเกี่ยวกับกระบวนการและหน่วยความจำไม่เสมือนหรือไม่ได้รับการป้องกัน TSR เป็นแฮ็คที่มีประโยชน์ซึ่งสามารถบอกได้ว่าระบบใช้พื้นที่มากถึง 64K และจะเชื่อมต่อกับอินเทอร์รัปต์ดังนั้นพวกเขาจึงถูกเรียก
Blrfl

8

เช่นเดียวกับที่คนอื่น ๆ กล่าวไว้ระบบปฏิบัติการส่วนใหญ่จะเรียกคืนหน่วยความจำที่จัดสรรเมื่อสิ้นสุดกระบวนการ (และอาจเป็นทรัพยากรอื่น ๆ เช่นซ็อกเก็ตเครือข่ายที่จับไฟล์ ฯลฯ )

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

คุณธรรมของเรื่องราวควรทำความสะอาดหลังจากตัวเองเสมอ อย่าปล่อยให้ของห้อย อย่าพึ่งพาการล้างระบบปฏิบัติการหลังจากคุณ ทำความสะอาดหลังตัวเอง.


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

1
ใน C ++ ผู้ทำลายจะถูกเรียกร้องให้ยุติโปรแกรม (ยกเว้นกรณีที่kill -9พัดลมน้อยกว่าสว่างปรากฏขึ้น ... )
vonbrand

@vonbrand True แต่ถ้าเรากำลังพูดถึงการรั่วไหลด้วยวัตถุไดนามิกตัวทำลายเหล่านั้นจะไม่เกิดขึ้น วัตถุที่อยู่นอกขอบเขตเป็นตัวชี้ดิบและตัวทำลายของมันคือ no-op (แน่นอนดูวัตถุ RAII เพื่อบรรเทาปัญหานี้ ... )
Andre Kostur

1
ปัญหาของ RAII คือมันยืนยันในการยกเลิกการจัดสรรอ็อบเจ็กต์ในการออกจากกระบวนการซึ่งไม่สำคัญที่จะต้องกำจัด การเชื่อมต่อ DB ที่คุณต้องระวัง แต่ระบบปฏิบัติการจะล้างหน่วยความจำทั่วไปได้ดีที่สุด (ทำได้ดีกว่ามาก) ปัญหานี้แสดงให้เห็นว่าเป็นโปรแกรมที่ต้องใช้เวลานานมากในการออกเมื่อหน่วยความจำจำนวนที่เพจหมดไป การแก้ปัญหาก็ไม่สำคัญเช่นกัน…
Donal Fellows

@vonbrand: มันไม่ง่ายอย่างนั้น std::exitจะเรียก dtors std::abortไม่ได้ข้อยกเว้นที่ไม่ถูกจับได้
MSalters

7

สิ่งนี้น่าจะขึ้นอยู่กับระบบปฏิบัติการมากกว่าภาษา ในที่สุดโปรแกรมใด ๆ ในภาษาใด ๆ ก็จะได้รับหน่วยความจำจากระบบปฏิบัติการ

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


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

@ulidtko สิ่งนี้จะทำให้เกิดความเสียหาย หากโปรแกรมของฉันต้องการให้พูด 1GiB นาน ๆ ครั้งและเข้าใจว่าในช่วงเวลาดังกล่าวแสดงว่าเป็นการปฏิเสธการใช้ทรัพยากรเหล่านั้นกับผู้อื่นแม้ว่าจะไม่ได้ใช้ก็ตาม วันนี้อาจมีความสำคัญหรือไม่ แต่สภาพแวดล้อมจะเปลี่ยนไปอย่างรุนแรง รับประกัน
vonbrand

@vonbrand การใช้ 1GiB แบบหายากไม่ใช่ปัญหาตามปกติ (ตราบเท่าที่คุณมีหน่วยความจำกายภาพมากมาย) เนื่องจากระบบปฏิบัติการที่ทันสมัยสามารถคัดลอกบิตที่ไม่ได้ใช้งานอยู่ในขณะนี้ ปัญหาเกิดขึ้นเมื่อคุณมีหน่วยความจำเสมือนในการใช้งานมากกว่าที่คุณมีหน่วยความจำกายภาพที่จะโฮสต์
Donal Fellows

5

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

ในทางกลับกันการปล่อยหน่วยความจำทั้งหมดอาจส่งผลต่อประสิทธิภาพการล้างข้อมูลของโปรแกรม

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

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


5

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

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

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


3

หน่วยความจำที่ใช้ร่วมกันบนระบบที่สอดคล้องกับ POSIX จะยังคงอยู่จนกว่าจะมีการเรียกใช้ shm_unlink หรือระบบจะรีบูต


2

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

ยกตัวอย่างครั้งหนึ่งฉันเคยทดลองพิมพ์ไปยังเครื่องพิมพ์ PDF ใน Java เมื่อฉันยกเลิก JVM ระหว่างงานเครื่องพิมพ์กระบวนการ spooling PDF ยังคงทำงานอยู่และฉันต้องฆ่ามันในตัวจัดการงานก่อนที่จะทำได้ ลองพิมพ์อีกครั้ง

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