ว่าด้วยเรื่อง Application Flag ที่ชื่อว่า "Large Heap"

Large Heap เป็นหนึ่งใน Application Flag ที่นักพัฒนาสามารถเปิดใช้งานเพื่อให้ระบบแอนดรอยด์เตรียมพื้นที่ว่างใน Heap สำหรับแอปเราให้มากกว่าเดิมจะได้รองรับการทำงานที่ต้องใช้พื้นที่ใน Heap ที่เพิ่มมากขึ้น

<manifest ...>
    <application
        android:largeHeap="true"
        ... >
        <!-- ... -->
    </application>
</manifest>

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

การใช้คำว่า "Memory" ในการพัฒนาแอปบนแอนดรอยด์มักจะหมายถึง Heap แต่เพื่อไม่ให้สับสนระหว่าง Heap กับ Stack ในบทความนี้จึงขอเจาะจงด้วยการใช้คำว่า "Heap" ไปเลย

Heap Size ในอุปกรณ์แอนดรอยด์

Heap คือ Memory ที่ระบบแอนดรอยด์เตรียมไว้ให้ตอน Runtime เพื่อให้แอปสามารถใช้ในระหว่างการทำงานได้ ซึ่งอุปกรณ์แอนดรอยด์แต่ละยี่ห้อ/รุ่น ก็จะมี Heap Size ที่ไม่เท่ากัน ขึ้นอยู่กับผู้ผลิตจะกำหนดค่าไว้ใน Firmware

และโดยปกติ Heap Size จะสัมพันธ์กับสเปคอย่างอื่นภายในเครื่องด้วย ทำให้เครื่องที่มี RAM น้อยก็จะมีหน้าจอความละเอียดต่ำด้วยเช่นกัน จึงเป็นเหตุผลว่าทำไมอุปกรณ์แอนดรอยด์แต่ละยี่ห้อ/รุ่นจึงมี Heap Size ที่ไม่เท่ากัน

และการที่อุปกรณ์แอนดรอยด์ที่มี RAM มากถึง 8GB ก็ไม่ได้หมายความว่าแอปของเราจะใช้งานได้เต็มที่ เพราะระบบแอนดรอยด์ต้องเผื่อไว้สำหรับการทำงานของตัว OS และแอปอื่น ๆ ที่อยู่ภายในเครื่องด้วย

ดังนั้นอุปกรณ์แอนดรอยด์แต่ละรุ่น/ยี่ห้อก็จะมีขนาดเริ่มต้นของ Heap สำหรับแต่ละแอปอยู่

Device Name Android Version RAM (GB) Default Heap (MB) Large Heap (MB)
LG Nexus 5X 8.1 2 192 512
Google Pixel 3 12 3 256 512
OPPO A18 13 4 + 4 384 512
Samsung Galaxy Z Flip5 14 8 256 512
Samsung Galaxy Tab S9 14 12 512 512

ควรแก้ปัญหาที่ต้นเหตุมากกว่าเปิดใช้ Large Heap

ในปัจจุบันมีแอปจำนวนไม่น้อยที่แก้ปัญหา Out of Memory (OOM) ด้วยการเปิดใช้งาน Large Heap เพื่อเพิ่ม Heap Size ให้มากขึ้น ทั้ง ๆ ที่ระบบแอนดรอยด์กำหนด Heap Size นั้นเพียงพอต่อการทำงานสำหรับแอปส่วนใหญ่อยู่แล้ว

แนวคิดนี้อาจจะใช้ได้กับแอปที่พัฒนาด้วย Native Android ถ้าใช้ Mobile Cross-platform Framework ก็อาจจะต้องพิจารณาเรื่อง Overhead Memory Usage จาก Framework ที่ใช้ด้วย

ดังนั้นการเปิดใช้งาน Large Heap เพียงเพราะเจอปัญหา Out of Memory ภายในแอปจึงเป็นวิธีแก้ปัญหาที่ปลายเหตุมากกว่า และต้นเหตุที่แท้จริงก็อาจจะมาจากการที่แอปจัดการกับการทำงานที่ต้องใช้ Memory ไม่ถูกต้อง ทำให้ Consume Memory เกินเท่าที่ควรจะเป็น

จากต้นเหตุที่อาจจะมาจากโค้ดแค่เพียงกี่จุด การเปิด Large Heap จะทำให้นักพัฒนาเขียนโค้ดที่จัดการกับ Memory ไม่ถูกต้องเพิ่มขึ้นในอนาคตได้ และเมื่อถึงจุดหนึ่งที่เจอปัญหา Out of Memory อีกครั้ง ก็จะพบว่าปัญหาที่ต้องแก้มีเยอะและยากกว่าก่อนที่จะเปิดใช้งาน Large Heap เสียอีก

วิเคราะห์การใช้ Memory ภายในแอปด้วย Profiler บน Android Studio

เพราะการ Debugging เพื่อหาสาเหตุของการ Consume Memory เกินจำเป็น นั้นเป็นเรื่องยากและต้องใช้เวลานานจึงทำให้นักพัฒนาส่วนใหญ่เลือกที่จะเปิดใช้งาน Large Heap นั่นเอง

ซึ่งในความเป็นจริง Android Studio ก็มีเครื่องมืออย่าง Profiler ที่ช่วยให้นักพัฒนาสามารถวิเคราะห์ Memory Allocation ที่เกิดขึ้นภายในแอปของเราได้

และถึงแม้นักพัฒนาจะไม่ใช่คนที่เชี่ยวชาญเรื่อง Memory ของ JVM หรือการทำงานของ Garbage Collector มากพอที่จะดูข้อมูลใน Profiler แล้วรู้สาเหตุได้ทันที แต่อย่างน้อยเราก็ดูภาพรวมของ Memory Allocation ได้ว่าการทำงานตรงจุดไหนในแอปที่ทำให้กราฟของ Memory Usage เพิ่มขึ้นจนผิดปกติ หรือทำให้ Garbage Collector ทำงานบ่อยเกินไป เพื่อลดขอบเขตของปัญหาไปเรื่อย ๆ จนกว่าจะเจอการโค้ดที่เป็นต้นเหตุของปัญหา

Inspect your app’s memory usage with Memory Profiler | Android Studio | Android Developers
Find out about the Memory Profiler component in the Android Profiler that helps you identify memory leaks and memory churn that can lead to stutter, freezes, and even app crashes.

สรุป

ถ้าแอปของคุณประสบปัญหา Out of Memory และแอปไม่ได้มีการทำงานที่จำเป็นต้องใช้ Memory หรือ Heap เป็นจำนวนเยอะมาก ก็ควรใช้วิธีอื่นเพื่อวิเคราะห์และจัดการกับปัญหาที่ต้นเหตุให้ถูกต้องตั้งแต่แรกดีกว่า

เพราะการเปิดใช้งาน Large Heap เพื่อแก้ปัญหาชั่วคราวจะเป็นการเลื่อนปัญหาไปให้อนาคตแทน และเมื่อถึงจุดนั้นก็อาจจะแก้ได้ยากจนกลายเป็น Technical Debt ที่เราต้องจ่ายในราคาที่แพงขึ้นอีกหลายเท่าตัวก็เป็นได้