เงื่อนข้อตกลงในการใช้บทความนี้
การเข้าใจถึงประเภทของหน่วยความจำนั้นมีความสำคัญมากต่อการบริหารจัดการ Memory ในการเขียนโปรแกรมให้มีประสิทธิภาพ ซึ่งมือใหม่หลายคนนั้นยังไม่เข้าใจและอาจจะไม่ให้ความสำคัญมากนักเพราะเห็นว่าเป็นเรื่องไกลตัวแต่จริงๆ แล้วนั้นใช้บ่อยๆ ครับ
เพื่อให้อธิบายเข้าใจโดยง่ายผมจะขอยกตัวอย่างเปรียบเทียบกับการทำโจทย์คณิตศาสตร์นะครับ
1. Stack
คิดง่ายๆ ว่าเป็นกระดาษทำโจทย์เลขที่เราต้องเขียนค่าต่างๆ ลงไปในการทำโจทย์
2. Stack frame
เป็นช่วงบรรทัดที่เราจะใช้เขียนสูตรและค่าลงไปในการดาษทำโจทย์ เป็น subset ของ Stack
3. Heap
ว่าง่ายๆ มันคือกระดาษทดในการคิดเลข จำเป็นจะต้องมีการจองพื้นที่ที่จะใช้งานและแจ้งคืนพื้นที่ที่ไม่ใช้งานแล้วเพราะกระดาษทดใบนี้ใช้ร่วมกันหลายคน
4. Registry
เปรียบเทียบง่ายๆ คือพื้นที่ความจำที่เราใช้คิดเลขในใจครับ สามารถทำการคำนวณได้รวดเร็วแต่เก็บค่าได้ไม่มากนัก
ดังนี้ 1 โปรแกรมจะมี Stack 1 ตัวและมี Stack frame ย่อยๆ อีกมากมายซึ่ง 1 Stack frame ต่อ 1 method ครับ
Stack frame นั้นจะถูกทำลายทันทีหลังจากจบ method แล้ว ค่าต่างๆ ที่อยู่ในนี้เองก็จะถูกทำลายไปด้วยเช่นกัน
Heap นั้นไม่ขึ้นอยู่กับ method ใดๆ เพราะเป็นการดึงหน่วยความจำที่ว่างอยู่มาใช้งานชั่วคราว
ส่วน Registry นั้นในหลายภาษาไม่สามารถกำหนดใช้เองได้ยกเว้นบางภาษาเช่น C และ C++
Memory leak หรือก็คือการจองพื้นที่เอาไว้โดยไม่ได้ใช้งานอะไรซึ่งส่งผลทำให้ใช้ Ram มากเกินความจำเป็นโดยใช่เหตุซึ่งสาเหตุหลักๆ ของปัญหานี้นั้นได้แก่
1. ทำการจองพื้นที่ใน Heap แล้วลืมส่งคืน มักเกิดขึ้นในภาษาเช่นภาษา C ตลอดจนการเลือกที่จะสร้าง Dynamic memory เองแล้วจัดการไม่รัดกุมพอ
2. การสร้างค่าบน Stack frame โดยไม่คำนึงถึงการทำลาย Stack frame ซึ่งจุดนี้นั้นเป็นกันเยอะเนื่องจากไม่ค่อยมีใครใส่ใจตรงจุดและสามารถเกิดขึ้นได้ทุกภาษาตัวอย่างเช่น
Dim A = 5
Dim B = 10
Dim C = Power(A, B) '// A
B
Dim D = Factorial(C)
Worker(A, B, C, D)
ในตัวอย่างนี้นั้นประกอบไปด้วยตัวแปร Integer 4 ตัว(A, B, C, D)แต่ละตัวมีขนาด 4 bytes ซึ่งรวมแล้ว 16 bytes + พื้นที่พักรับส่งค่าซึ่งจะใช้ค่าสูงสุดซึ่งก็คือ 4 จาก Worker method และค่าแต่ละตัวนั้นเป็น Integer เช่นกันจึงรวมเป็น 16 bytes
ดังนี้ Method นี้จะสร้าง Stack frame ขนาด 16 + 16 = 32 bytes
ซึ่งพื้นที่จำนวน 32 bytes นี้นั้นจะถูกตีตราว่าใช้งานอยู่จนกว่า Worker method จะทำงานเสร็จ หากเขียน Worker method โดยไม่คำนึงปล่อยให้ทำไปยืดยาวแล้วก็จะเกิดการสูญปล่าวไปเรื่อยๆ และยิ่งถ้ามีการเรียกใช้ Method นี้จาก Thread ต่างๆ ก็จะยิ่งทำให้เพิ่มการใช้ ram โดยไม่จำเป็นมากขึ้นเรื่อยๆ
ดังนี้ method ที่มีการเรียกใช้บ่อยและคงอยู่ยาวนานนั้นไม่ควรสร้างค่าอะไรลงไปเท่าที่จะทำได้ อย่าง main() เองนั้นก็ไม่ควรจะสร้างค่าอะไรมากเช่นกัน หรือพวก Recursive method ซึ่งสามารถบรรเทาได้ด้วยการให้ทำการแยกส่วนไปสร้าง method เล็กๆ แล้วเรียกใช้แทน
หมายเหตุ :: เหตุในข้อ 2 นี้สามารถแก้ไขได้ด้วยการ invoke method ด้วย jmp(Jump)/tail call แทนการ call ทั่วไป
หากบทความนี้ถูกใจ เป็นประโยชน์ ขอรบกวนผู้อ่านกด [+] ด้วยนะครับ ขอบคุณครับ
[Tutorial] สอนภาษา .Net :: ตอนที่ 07 หน่วยความจำประเภทต่างๆ Heap, Stack และ Registry
การเข้าใจถึงประเภทของหน่วยความจำนั้นมีความสำคัญมากต่อการบริหารจัดการ Memory ในการเขียนโปรแกรมให้มีประสิทธิภาพ ซึ่งมือใหม่หลายคนนั้นยังไม่เข้าใจและอาจจะไม่ให้ความสำคัญมากนักเพราะเห็นว่าเป็นเรื่องไกลตัวแต่จริงๆ แล้วนั้นใช้บ่อยๆ ครับ
เพื่อให้อธิบายเข้าใจโดยง่ายผมจะขอยกตัวอย่างเปรียบเทียบกับการทำโจทย์คณิตศาสตร์นะครับ
1. Stack
คิดง่ายๆ ว่าเป็นกระดาษทำโจทย์เลขที่เราต้องเขียนค่าต่างๆ ลงไปในการทำโจทย์
2. Stack frame
เป็นช่วงบรรทัดที่เราจะใช้เขียนสูตรและค่าลงไปในการดาษทำโจทย์ เป็น subset ของ Stack
3. Heap
ว่าง่ายๆ มันคือกระดาษทดในการคิดเลข จำเป็นจะต้องมีการจองพื้นที่ที่จะใช้งานและแจ้งคืนพื้นที่ที่ไม่ใช้งานแล้วเพราะกระดาษทดใบนี้ใช้ร่วมกันหลายคน
4. Registry
เปรียบเทียบง่ายๆ คือพื้นที่ความจำที่เราใช้คิดเลขในใจครับ สามารถทำการคำนวณได้รวดเร็วแต่เก็บค่าได้ไม่มากนัก
ดังนี้ 1 โปรแกรมจะมี Stack 1 ตัวและมี Stack frame ย่อยๆ อีกมากมายซึ่ง 1 Stack frame ต่อ 1 method ครับ
Stack frame นั้นจะถูกทำลายทันทีหลังจากจบ method แล้ว ค่าต่างๆ ที่อยู่ในนี้เองก็จะถูกทำลายไปด้วยเช่นกัน
Heap นั้นไม่ขึ้นอยู่กับ method ใดๆ เพราะเป็นการดึงหน่วยความจำที่ว่างอยู่มาใช้งานชั่วคราว
ส่วน Registry นั้นในหลายภาษาไม่สามารถกำหนดใช้เองได้ยกเว้นบางภาษาเช่น C และ C++
Memory leak หรือก็คือการจองพื้นที่เอาไว้โดยไม่ได้ใช้งานอะไรซึ่งส่งผลทำให้ใช้ Ram มากเกินความจำเป็นโดยใช่เหตุซึ่งสาเหตุหลักๆ ของปัญหานี้นั้นได้แก่
1. ทำการจองพื้นที่ใน Heap แล้วลืมส่งคืน มักเกิดขึ้นในภาษาเช่นภาษา C ตลอดจนการเลือกที่จะสร้าง Dynamic memory เองแล้วจัดการไม่รัดกุมพอ
2. การสร้างค่าบน Stack frame โดยไม่คำนึงถึงการทำลาย Stack frame ซึ่งจุดนี้นั้นเป็นกันเยอะเนื่องจากไม่ค่อยมีใครใส่ใจตรงจุดและสามารถเกิดขึ้นได้ทุกภาษาตัวอย่างเช่น
Dim A = 5
Dim B = 10
Dim C = Power(A, B) '// AB
Dim D = Factorial(C)
Worker(A, B, C, D)
ในตัวอย่างนี้นั้นประกอบไปด้วยตัวแปร Integer 4 ตัว(A, B, C, D)แต่ละตัวมีขนาด 4 bytes ซึ่งรวมแล้ว 16 bytes + พื้นที่พักรับส่งค่าซึ่งจะใช้ค่าสูงสุดซึ่งก็คือ 4 จาก Worker method และค่าแต่ละตัวนั้นเป็น Integer เช่นกันจึงรวมเป็น 16 bytes
ดังนี้ Method นี้จะสร้าง Stack frame ขนาด 16 + 16 = 32 bytes
ซึ่งพื้นที่จำนวน 32 bytes นี้นั้นจะถูกตีตราว่าใช้งานอยู่จนกว่า Worker method จะทำงานเสร็จ หากเขียน Worker method โดยไม่คำนึงปล่อยให้ทำไปยืดยาวแล้วก็จะเกิดการสูญปล่าวไปเรื่อยๆ และยิ่งถ้ามีการเรียกใช้ Method นี้จาก Thread ต่างๆ ก็จะยิ่งทำให้เพิ่มการใช้ ram โดยไม่จำเป็นมากขึ้นเรื่อยๆ
ดังนี้ method ที่มีการเรียกใช้บ่อยและคงอยู่ยาวนานนั้นไม่ควรสร้างค่าอะไรลงไปเท่าที่จะทำได้ อย่าง main() เองนั้นก็ไม่ควรจะสร้างค่าอะไรมากเช่นกัน หรือพวก Recursive method ซึ่งสามารถบรรเทาได้ด้วยการให้ทำการแยกส่วนไปสร้าง method เล็กๆ แล้วเรียกใช้แทน
หมายเหตุ :: เหตุในข้อ 2 นี้สามารถแก้ไขได้ด้วยการ invoke method ด้วย jmp(Jump)/tail call แทนการ call ทั่วไป
หากบทความนี้ถูกใจ เป็นประโยชน์ ขอรบกวนผู้อ่านกด [+] ด้วยนะครับ ขอบคุณครับ