স্মৃতি পুনরুদ্ধার: উচ্ছেদ এবং অদলবদল

পূর্ববর্তী অধ্যায়গুলোতে আমরা দেখেছি, কার্নেল কীভাবে সিস্টেম-ব্যাপী মেমোরি পরিচালনা করে। এই অধ্যায়ে আমরা মেমোরি কন্ট্রোল গ্রুপ (memcg) নিয়ে আরও বিস্তারিত আলোচনা করব, যা অ্যান্ড্রয়েড প্রতিটি অ্যাপের জন্য মেমোরির ব্যবহার বিভাজন ও নিয়ন্ত্রণ করতে ব্যবহার করে।

memcg বোঝা অত্যন্ত গুরুত্বপূর্ণ, কারণ এই স্তরেই সিস্টেম নির্দিষ্ট অ্যাপকে মেমরি পুনরুদ্ধারের জন্য লক্ষ্য করতে পারে বা সেগুলোর উপর মেমরি সীমা নির্ধারণ করতে পারে, যা অ্যাপের মেমরি ব্যবহারের সক্রিয় ব্যবস্থাপনার সুযোগ করে দেয়।

স্মৃতি নিয়ন্ত্রণ গোষ্ঠী (memcg)

কন্ট্রোল গ্রুপ (cgroups) হলো লিনাক্স কার্নেলের একটি বৈশিষ্ট্য, যা প্রসেসগুলোকে স্তরভিত্তিক দলে সংগঠিত করতে এবং তাদের মধ্যে সিস্টেম রিসোর্স (যেমন সিপিইউ, মেমরি এবং আই/ও) বন্টন করতে সাহায্য করে। memcg হলো বিশেষভাবে মেমরির জন্য তৈরি cgroup কন্ট্রোলার।

মূল মেমসিজি ধারণা

  • Memcg চার্জ : যখন একটি memcg-এর অন্তর্ভুক্ত কোনো প্রসেস মেমরির একটি পেজ (অ্যানোনিমাস বা ফাইল-ব্যাকড) বরাদ্দ করে, তখন সেই পেজটি memcg-এর জন্য "চার্জ" করা হয়। একটি memcg-এর মোট চার্জ হলো এর অন্তর্ভুক্ত সমস্ত প্রসেস দ্বারা ব্যবহৃত সমস্ত পেজের সমষ্টি।
  • ক্রমিক হিসাবরক্ষণ : মেমোরি ব্যবহারের হিসাব ট্রি-এর ওপরের স্তর পর্যন্ত করা হয়। একটি চাইল্ড সি-গ্রুপের চার্জ তার প্যারেন্টের ব্যবহারের ক্ষেত্রেও গণনা করা হয়।
  • মেমরি সীমা : প্রতিটি memcg-এর সীমা থাকতে পারে (যেমন memory.max বা memory.high ), যা অতিক্রম করলে গ্লোবাল সিস্টেম মেমরি থেকে স্বাধীনভাবে রিক্লেইম বা এমনকি OOM কিলার সক্রিয় হয়।
  • প্রতি-memcg পুনরুদ্ধার : যখন একটি memcg তার সীমা অতিক্রম করে, অথবা যখন সিস্টেমের মেমরির প্রয়োজন হয়, তখন কার্নেল পুনরুদ্ধারের জন্য একটি নির্দিষ্ট memcg-কে লক্ষ্য করতে পারে। এর অর্থ হলো এর ফাইল পেজগুলিকে সরিয়ে দেওয়া অথবা এর অ্যানোনিমাস পেজগুলিকে ZRAM-এ সোয়াপ করা।

অ্যান্ড্রয়েড memcg শ্রেণিবিন্যাস

অ্যান্ড্রয়েড অ্যাপ প্রসেসগুলো পরিচালনা করার জন্য একটি নির্দিষ্ট স্তরবিন্যাস ব্যবহার করে। এই কাঠামোটি সিস্টেমকে বিভিন্ন ধরনের অ্যাপের (যেমন, ফোরগ্রাউন্ড বনাম ব্যাকগ্রাউন্ড) জন্য ভিন্ন ভিন্ন নীতি প্রয়োগ করার সুযোগ দেয়।

অ্যান্ড্রয়েড মেমসিজি হায়ারার্কি

  • /sys/fs/cgroup/apps/ : সকল অ্যান্ড্রয়েড অ্যাপ্লিকেশনের মূল ঠিকানা।
  • uid_<UID>/ : প্রতিটি অ্যাপের ইউজার আইডির জন্য একটি ডিরেক্টরি। একই অ্যাপ প্যাকেজের অন্তর্গত সমস্ত প্রসেস এই গ্রুপটি ব্যবহার করে।
  • pid_<PID>/ : প্রতিটি স্বতন্ত্র প্রসেস এবং তা থেকে ফোর্ক করা যেকোনো চাইল্ড প্রসেসের জন্য একটি ডিরেক্টরি। এটি একাধিক প্রসেসযুক্ত অ্যাপের জন্য সূক্ষ্ম নিয়ন্ত্রণ এবং হিসাবরক্ষণের সুযোগ দেয়।

হাতে-কলমে অনুশীলন: memcg অন্বেষণ

এই অনুশীলনীতে, আপনি MemoryLab- এর memcg ডিরেক্টরিটি খুঁজে বের করবেন এবং রিয়েল-টাইমে এর মেমোরি চার্জ পর্যবেক্ষণ করবেন।

১. মেমোরিল্যাব চালু করুন

আপনার ডিভাইসে MemoryLab চালু আছে কিনা তা নিশ্চিত করুন।

২. মেমোরিল্যাবের memcg খুঁজুন।

প্রথমে, চলমান MemoryLab অ্যাপটির PID সংগ্রহ করুন:

adb shell pidof com.android.memorylab
# Example output: 11672

এখন, এর cgroup ডিরেক্টরিটি সনাক্ত করুন। আপনি এটি /proc/<PID>/cgroup এ পাবেন:

adb shell cat /proc/11672/cgroup
# Example output: 0::/apps/uid_10274/pid_11672

cgroup v2-তে, 0:: এর পরের পাথটি /sys/fs/cgroup সাপেক্ষে memcg হায়ারার্কি নির্দেশ করে। সুতরাং সম্পূর্ণ পাথটি হলো /sys/fs/cgroup/apps/uid_10274/pid_11672/

৩. মেমসিজি পরিসংখ্যান পড়ুন

ওই ডিরেক্টরিতে প্রবেশ করুন এবং মূল ফাইলগুলো দেখুন:

# Current memory usage (in bytes)
adb shell cat /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.current
# Example output:
# 1591324672

memory.current এই cgroup-এ বর্তমানে ব্যবহৃত মোট মেমোরির পরিমাণ (বাইটে) দেখায়।

# Detailed statistics
adb shell cat /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.stat | head -n 10
# Example output:
# anon 1585098752
# file 741376
# kernel 5484544
# kernel_stack 393216
# pagetables 4583424
# sec_pagetables 0
# percpu 216
# sock 0
# vmalloc 4096
# shmem 20480

memory.stat একটি বিশদ বিবরণ প্রদান করে: * anon : অ্যানোনিমাস মেমরির (হিপ, স্ট্যাক) পরিমাণ। * file : ফাইল-ব্যাকড মেমরির (পেজ ক্যাশ) পরিমাণ। * swap : ZRAM-এ সোয়াপ আউট করা মেমরির পরিমাণ।

৪. পরিবর্তনসমূহ পর্যবেক্ষণ করুন

  1. ওপেন মেমোরিল্যাব
  2. memory.current এর মানটি লক্ষ্য করুন।
  3. Allocate Java Memory (10MB) অপশনটিতে বেশ কয়েকবার ট্যাপ করুন।
  4. memory.current আবার পড়ুন। আপনি দেখবেন যে প্রতিবার ট্যাপ করার ফলে এটি প্রায় ১০ মেগাবাইট করে বাড়ছে।
  5. Allocate Bitmaps- এ ট্যাপ করুন। anon বৃদ্ধি দেখতে memory.stat চেক করুন।

memory.reclaim এর মাধ্যমে সক্রিয়ভাবে পুনরুদ্ধার করুন।

memcg (v2)-এর একটি শক্তিশালী বৈশিষ্ট্য হলো memory.reclaim ফাইল। এই ফাইলে কোনো মান লিখলে তা কার্নেলকে নির্দেশ দেয় যেন সে অবিলম্বে এই memcg এবং এর অধীনস্থ যেকোনো memcg থেকে সেই পরিমাণ মেমরি পুনরুদ্ধার করার চেষ্টা করে।

অ্যান্ড্রয়েড কীভাবে memory.reclaim ব্যবহার করে।

অ্যান্ড্রয়েডের CachedAppOptimizer অ্যাপগুলো ফ্রিজ হয়ে যাওয়ার পর সেগুলোর থেকে সর্বোচ্চ পরিমাণ মেমরি পুনরুদ্ধার করতে এই ফিচারটি ব্যবহার করে। অ্যান্ড্রয়েড অ্যাপ ফ্রিজার নিশ্চিত করে যে, ক্যাশ করা অ্যাপগুলো যখন চালু থাকে না, তখন যেন সেগুলো যথাসম্ভব কম র‍্যাম ব্যবহার করে। যখন কোনো অ্যাপ ব্যাকগ্রাউন্ডে চলে যায় এবং ফ্রিজ হয়ে যায়, তখন সিস্টেম অ্যাপটির বর্তমান মেমরি ব্যবহারের তথ্য তার memory.reclaim ফাইলে লিখে রাখে। এটি কার্নেলকে সমস্ত সম্ভাব্য ফাইল পেজ সরিয়ে দিতে এবং সমস্ত অ্যানোনিমাস পেজকে ZRAM-এ সোয়াপ করতে বাধ্য করে, যার ফলে অ্যাপটির রেসিডেন্ট ফুটপ্রিন্ট ন্যূনতম পর্যায়ে চলে আসে।

আপনি ম্যানুয়ালি memory.current রিড করে এবং তা memory.reclaim এ আবার রাইট করার মাধ্যমে একই "সর্বোচ্চ রিক্লেইম" অর্জন করতে পারেন।

# Force reclaim of everything
adb shell "cat /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.current > /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.reclaim"

অনুশীলন: বলপূর্বক পুনরুদ্ধার এবং সন্ধান

আমরা এখন কার্নেলকে মেমোরিল্যাব থেকে মেমোরি পুনরুদ্ধার করতে বাধ্য করব এবং একটি পারফেটটো ট্রেসে এই কার্যকলাপটি ধারণ করব।

  1. প্রস্তুতি : নিশ্চিত করুন যে মেমোরিল্যাবে কিছু মেমোরি অ্যালোকেশন (জাভা এবং বিটম্যাপ) করা আছে।
  2. ট্রেসিং শুরু করুন : শিডিউলিং, রিক্লেইম এবং পেজ ক্যাশ ইভেন্টগুলো ক্যাপচার করতে এই ইনলাইন ট্রেস কনফিগারেশনটি ব্যবহার করুন।

    # Use a config that captures scheduling, reclaim and page cache events
    adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/reclaim.perfetto-trace <<EOF
    buffers: {
        size_kb: 131072
        fill_policy: RING_BUFFER
    }
    data_sources: {
        config {
            name: "linux.ftrace"
            ftrace_config {
                ftrace_events: "sched/sched_switch"
                ftrace_events: "sched/sched_wakeup"
                ftrace_events: "kmem/rss_stat"
                ftrace_events: "mm_filemap_add_to_page_cache"
                ftrace_events: "mm_filemap_delete_from_page_cache"
                ftrace_events: "vmscan/mm_vmscan_direct_reclaim_begin"
                ftrace_events: "vmscan/mm_vmscan_direct_reclaim_end"
                ftrace_events: "vmscan/mm_vmscan_memcg_reclaim_begin"
                ftrace_events: "vmscan/mm_vmscan_memcg_reclaim_end"
                symbolize_ksyms: true
            }
        }
    }
    data_sources: {
        config {
            name: "linux.process_stats"
            process_stats_config {
                scan_all_processes_on_start: true
            }
        }
    }
    EOF
    
  3. ট্রিগার চাপ এবং পুনরুদ্ধার :

    • মেমোরিল্যাবে, 'Allocate Native Memory (1GB)' -এ ট্যাপ করুন। এটি সিস্টেম-ব্যাপী চাপ সক্রিয় করবে।
    • একটি পৃথক টার্মিনালে ২০০ মেগাবাইট পুনরুদ্ধার করুন:

      adb shell "echo 200M > /sys/fs/cgroup/apps/uid_10274/pid_11672/memory.reclaim"
      
  4. ফল্ট ব্যাক ইন : মেমোরিল্যাব- এ ফিরে যান। থ্র্যাশ পেজক্যাশ (রিফল্ট টেস্ট) -এ ট্যাপ করুন।

  5. ট্রেস বন্ধ করুন : ট্রেস টার্মিনালে Ctrl+C চাপুন।

পারফেটোতে পুনরুদ্ধার বিশ্লেষণ

যখন আপনি ট্রেসটি খুলবেন, তখন আপনি সিস্টেম-ব্যাপী এবং অ্যাপ-ভিত্তিক উভয় প্রকার রিক্লেইম কার্যকর হতে দেখতে পাবেন:

পারফেট্টো সরাসরি এবং মেমসিজি পুনরুদ্ধার দেখাচ্ছে

ট্রেস-এ নিম্নলিখিত বিষয়গুলো খুঁজুন:

  • kswapd : কার্নেল থ্রেড-এর অধীনে kswapd0 খুঁজুন (উপরের স্ক্রিনশটে এটিকে ম্যানুয়ালি সবার উপরে পিন করা হয়েছিল)। আপনি দেখবেন, সিস্টেম যখন খালি পেজ খুঁজে পেতে চেষ্টা করে, তখন এটি জেগে উঠছে এবং চলতে শুরু করছে (সবুজ স্লাইস)।
  • ডিরেক্ট রিক্লেইম : com.android.memorylab প্রসেস থ্রেডগুলো দেখুন। আপনি থ্রেডের শিডিউলিং ট্র্যাকের ঠিক নিচে বেগুনি রঙের ftrace ইভেন্ট স্লাইস (যেমন mm_vmscan_direct_reclaim_begin ) দেখতে পাবেন। এটি নির্দেশ করে যে অ্যাপ থ্রেডটি নিজেই কার্নেলের পেজ মুক্ত করার জন্য অপেক্ষা করতে গিয়ে আটকে আছে।
  • আরএসএস এবং সোয়াপ কাউন্টার :
    • mem.rss.anon : অ্যালোকেশন বাটনগুলোতে ট্যাপ করলে এর পরিমাণ বাড়ে।
    • mem.swap : kswapd এবং অ্যাপের নিজস্ব থ্রেডগুলো (সরাসরি রিক্লেইমের সাপেক্ষে) সেই অ্যানোনিমাস পেজগুলোকে ZRAM-এ কম্প্রেস করার ফলে এটি ক্রমাগত বাড়তে থাকে।
  • memcg রিক্লেইম : আপনি যদি ম্যানুয়াল রিক্লেইমটি ট্রিগার করার মুহূর্তটিতে জুম করেন, তাহলে rss.anon এবং rss.file উভয়েরই একটি তীব্র পতন দেখতে পাবেন, যার সাথে mm_vmscan_memcg_reclaim ইভেন্টগুলোও দেখা যাবে।

← সিস্টেম-ব্যাপী | ↑ উপরে | kswapd এবং lmkd-এর পারস্পরিক ক্রিয়া →