/ 365วันแห่งโปรแกรม

[365 วันแห่งโปรแกรม #day13] Memory

วันที่สิบสามของ ‪#‎365วันแห่งโปรแกรม‬ วันนี้เราจะมีคุยกันเรื่อง Memory


เมื่อเรารันโปรแกรมใดๆ โปรแกรมนั้นจะถูกโหลดขึน Memory แต่ทุกอย่างของโปรแกรมนั้นไม่ได้ถูกโหลดขึ้นไปแล้ววางกองที่เดียวกันนะ มันมีการแบ่งสัดส่วนของผืน memory โดย OS หรือ runtime ของโปรแกรมนั้นๆ

ขนาดของ memory ที่โปรแกรมหนึ่งๆ จะได้รับขึ้นอยู่กับ OS และ runtime ของภาษาโปรแกรมที่ใช้ โดย memory ก้อนนั้นยังจะถูกแบ่งออกเป็น segment เพื่อเอาไปใช้เก็บค่าที่ต่างกัน โดยในที่นี้เราจะสนใจแค่ Code segment, Stack segment, และ Heap segment

Code segment

Code segment หรือบางที่เรียก Text segment มีหน้าที่อย่างเดียวคือเก็บตัว executable instructions (ชุดคำสั่งของโปรแกรมที่นำไป execute ได้) โดยทั่วไปแล้วจะเป็นแบบ read-only และไม่สามารถปรับขนาดได้

Stack

Stack เป็นส่วนที่ใช้ในการเก็บฟังก์ชันที่ถูกเรียกขึ้นมาทำงานและตัวแปรพื้นฐานภายในฟังก์ชันนั้น (local primitive variable) พื้นที่ส่วนนี้จะมีโครงสร้างแบบ LIFO (Last In First Out) โดยมี pointer ตัวนึงเรียกว่า stack pointer ใช้สำหรับชี้ไปยงข้อมูลที่อยู่บนสุดของ stack

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

หากมีการเรียกฟังก์ชันอื่นจากฟังก์ชันที่กำลังทำงานอยู่ ฟังก์ชันใหม่ที่ถูกเรียกก็จะถูกโหลดเข้าไปยัง Stack ซ้อนขึ้นไปจากฟังก์ชันเดิม ซึ่งหมายความว่า stack frame ของเก่าจะยังไม่สามารถ pop ออกได้จนกว่า stack frame ที่อยู่เหนือกว่าจะถูก pop ไปหมดแล้ว

พื้นที่ของ Stack นั้นมีอยู่จำกัดและโดยทั่วไปไม่สามารถขยายขนาดระหว่าง runtime เมื่อโปรแกรมพยายามเรียกฟังก์ชันแล้วพื้นที่ใน Stack เหลือไม่พอก็จะเกิด stack overflow ในบางภาษาโปรแกรมยอมให้ตัวแปรแบบ complex สามารถเก็บลงใน Stack ได้ เช่น C++ จะยอมให้เก็บ object ลงใน Stack เมื่อ object นั้นไม่ได้ถูกสร้างขึ้นด้วย keyword new

ใน multi-thread programming ทุก thread ของโปรแกรมจะมี Stack memory ของตัวเอง

Heap

Heap เป็น memory อีกส่วนหนึ่ง ที่ใช้เก็บ complex object และ instance variable (ตัวแปรที่เป็นสมาชิกของคลาส) พื้นที่ในส่วนนี้ไม่มีการจัดการแบบอัตโนมัติแบบที่มีใน Stack กล่าวคือ การที่จะจองพื้นที่ใน Heap ก็ต้องเรียกใช้คำสั่งสำหรับจอง หรือการคืนื้นที่ (ลบ object ทิ้ง) ก็ต้องใส่คำสั่งสำหรับลบเอง ทั้งนี้ตัวภาษาโปแกรมอาจจะช่วยในการจัดการให้ง่ายขึ้นได้ เช่น มี garbage collection หากเราไม่มีการจัดการกับ object ที่ไม่ใช้แล้ว ก็จะเกิดปัญหา memory leak (พื้นที่ที่ถูกจองโดยสูญเปล่า ไม่ได้ใช้ และลบทิ้งไม่ได้)

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

พื้นที่ใน Heap นั้นสามารถขยายได้ ทุกครั้งที่พื้นที่ว่างไม่พอ OS สามารถสั่งเพิ่มพื้นทีให้ได้ แต่ถ้าไม่สามารถเพิ่มได้ ก็จะเกิด Out of memory exception

ในแง่ของความเร็ว Stack นั้นจะเร็วกว่า Heap มาก เพราะในการจองพื้นที่บน Stack ก็แค่เลื่อน Stack pointer ขึ้น เมื่อต้องการอกออก ก็แค่เลื่อน pointer ลง ต่างจาก Heap ที่ต้องไปค้นหาพื้นที่ว่างแล้วจึงจองได้

Memory explaination

รูปภาพประกอบมาจากบทความ QuickTip: Java Basics: Stack and Heap

‪#‎day13‬ #365วันแห่งโปรแกรม ‪#‎โครงการ365วันแห่ง‬...