การจะเป็นโปรแกรมเมอร์ที่เก่งกาจได้นั้น นอกจากจะต้องมีไหวพริบในการแก้ไขปัญหาแล้ว ยังมีอีก 1 ปัจจัย คือ ต้องใช้ทรัพยากรให้คุ้มค่าที่สุด ทรัพยากรที่ผมหมายถึงคือ Memory หรือ Ram นั่นเอง วันนี้ผมจะพูดถึง เรื่องเล็กๆ ที่ใครหลายๆ คนอาจจะมองข้าม นั่นก็คือ memory leak memory leak ถ้าแปลตามแบบฉบับ google คือ หน่วยความจำรั่วไหล ฮ่ะๆ ฟังดูแล้วแปลกๆ นะครับ จริงๆ memory leak คือการที่เราใช้หน่วยความจำเสร็จแล้ว แต่ไม่ยอมส่งคืนหน่วยความจำส่วนนั้นให้กับระบบ ปัญหาที่ตามมาคือ ระบบจะไม่มีหน่วยความจำให้ใช้ และจะทำให้เกิดอาการเครื่องแฮงค์หรือค้างได้ครับ วิธีแก้คือ จองหน่วยความจำ -> ใช้ -> คืน 3 ขั้นตอนง่ายๆ จำให้ขึ้นใจนะครับ จอง-ใช้-คืน พยายามอย่าดอง หรือ กั๊ก หน่วยความจำนะครับ
     ใน C# จะไม่ค่อยเจอปัญหานี้สักเท่าไหร่ครับ เพราะตัว C# มีระบบจัดการหน่วยความจำที่ดีมากครับ ต่างกับ C++ แบบสุดขั้วเลยครับ แต่ก็อย่าดีใจไปครับ เพราะ Emgu บน C# นั้นมีอยู่ method หนึ่งที่เราใช้กันบ่อยมาก และ method นี้ เราต้องจองหน่วยความจำ ก่อนเพื่อจะใช้งาน นั้นก็คือ cvFindContours เดี๋ยวผมโชว์ความเลวร้ายของ memory leak ให้ดูกันนะครับ ผมจะแยกเคสการทดลองออกเป็น 2 เคสนะครับ เคสแรกคือ จองหน่วยความจำ->แล้วไม่คืน อีกเคสคือ จองหน่วยความจำ->ส่งคืนหน่วยความจำ

เคสที่ 1: จองหน่วยความจำแล้วไม่ยอมคืน
ผมจะใช้ code นี้เพื่อทดสอบครับ จาก code ด้านล่างจะเห็นได้ว่า ผมขอจอง memory โดยใช้ cvCreateMemStorage(0) หลังจากนั้นก็ทำอะไรสักอย่าง เพื่อใช้ memory  เมื่อใช้เสร็จผมก็ไม่ได้ส่งคืนให้กับระบบ ทำแบบนี้เป็นลูปไปเรื่อยๆ แล้วเราจะมาดูกันว่าใน 1 นาที โปรแกรมนี้จะล้างผลาญ memory ของระบบไปเท่าไหร่

            Image<Gray, Byte> ContImage = inputImage.Convert<Gray, Byte>();
            int i = 0;
            while (true)
            {
                IntPtr storage = CvInvoke.cvCreateMemStorage(0);  // จองหน่วยความจำ
                IntPtr contour = new IntPtr();
                CvInvoke.cvFindContours(ContImage, storage, ref contour, System.Runtime.InteropServices.Marshal.SizeOf(typeof(MCvContour)), RETR_TYPE.CV_RETR_EXTERNAL, CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_NONE, new Point(0, 0));
                Seq<Point> seq = new Seq<Point>(contour, null);
                for (; seq != null && seq.Ptr.ToInt32() != 0; seq = seq.HNext)
                {
                    //do something
                }
                Console.WriteLine("Loop: "+i.ToString());
                i++;
            }

แค่รันผ่านไปแค่ 1 วิเอง เจ้าโปรแกรมนี้ก็ผลาญหน่วยความจำไปแล้ว 15.1 MB 


เมื่อผ่านไป 30 วินาที โปรแกรมนี้ก็ผลาญหน่วยตวามจำ ขึ้นแท่นเป็นอันดับหนึ่ง ด้วยยอดใช้หน่วยความจำไป 224.6 MB 


เมื่อรันโปรแกรมครบ 1 นาที เจ้าจอมล้างผลาญก็กินหน่วยความจำไป 428.3 MB  ไม่อยากจะคิด ถ้ารันทิ้งไว้สัก 5 นาที หรือ 10 นาที อะไรจะเกิดขึ้นกับคอมพิวเตอร์ของผม 



เคสที่ 2: จองหน่วยความจำ แล้วส่งคืนให้ระบบ
     เคสนี้ผมจะจองหน่วยความจำ และเมื่อใช้เสร็จผมจะคืนหน่วยความจำนั้นให้กับระบบ code ผมก็จะใช้เหมือนกับเคสที่ 1 แต่เพิ่มส่วนของการคืนหน่วยความจำให้กับระบบ cvReleaseMemStorage(ref storage) และเราก็จะมาจับตาดูกันมาเมื่อรันโปรแกรมไป 1 นาที ยอดการผลาญหน่วยความจำจะเป็นเท่าไหร่

            Image<Bgr, Byte> inputImage = new Image<Bgr, byte>("input.jpg");
            Image<Gray, Byte> ContImage = inputImage.Convert<Gray, Byte>();
            int i = 0;
            while (true)
            {
                IntPtr storage = CvInvoke.cvCreateMemStorage(0);  //จองหน่วยความจำ
                IntPtr contour = new IntPtr();
                CvInvoke.cvFindContours(ContImage, storage, ref contour, System.Runtime.InteropServices.Marshal.SizeOf(typeof(MCvContour)), RETR_TYPE.CV_RETR_EXTERNAL, CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_NONE, new Point(0, 0));
                Seq<Point> seq = new Seq<Point>(contour, null);
                for (; seq != null && seq.Ptr.ToInt32() != 0; seq = seq.HNext)
                {
                    //do something
                }
                Console.WriteLine("Loop: "+i.ToString());
                i++;
                CvInvoke.cvReleaseMemStorage(ref storage);  // คืนหน่วยความจำ
            }

เริ่มต้นที่ 8.2 MB ครับ

เมื่อเวลาผ่านไป 30 วินาที หน่วยความจำถูกใช้ไป 9.0 MB

รันได้ครบ 1 นาที หน่วยความจำก็ยังถูกใช้แค่  9.1 MB

อันนี้แถมครับ เมื่อรันไปครบ 3 นาที ยอดการใช้หน่วยความจำก็ยังคงที่ ที่ 9.1 MB


     เห็นไหมครับ ถ้าเราจองหน่วยความจำ แล้วไม่ยอมคืนให้กับระบบมันเลวร้ายขนาดไหน มันส่งผลต่อความเสถียรของโปรแกรมเราโดยตรงเลยครับ  ดังนั้นโปรดจำ 3 คำไว้ให้นะครับ จอง->ใช้->คืน

2 ความคิดเห็น

This comment has been removed by the author.
Reply

ขอบคุณครับ กำลังนั่งไล่โปรแกรมอยู่เลย เจอปัญหาแบบนี้เลยครับ เปิดทิ้งไว้ กิน Mem เป็น กิกเลย
เดี๋ยวจะลองเพิ่ม " CvInvoke.cvReleaseMemStorage(ref storage); " ดูนะครับ ^_^

Reply

Post a Comment

Templated by Blogger Items