আপনার অ্যাপের মেমরি পরিচালনা করুন

এই পৃষ্ঠায় আপনার অ্যাপের মধ্যে মেমরি ব্যবহার সক্রিয়ভাবে কমানোর উপায় ব্যাখ্যা করা হয়েছে। অ্যান্ড্রয়েড অপারেটিং সিস্টেম কীভাবে মেমরি পরিচালনা করে সে সম্পর্কে জানতে, ‘মেমরি ব্যবস্থাপনার সংক্ষিপ্ত বিবরণ’ দেখুন।

র‍্যান্ডম-অ্যাক্সেস মেমরি (RAM) যেকোনো সফটওয়্যার ডেভেলপমেন্ট এনভায়রনমেন্টের জন্য একটি মূল্যবান রিসোর্স, এবং মোবাইল অপারেটিং সিস্টেমের জন্য এটি আরও বেশি মূল্যবান, যেখানে ফিজিক্যাল মেমরি প্রায়শই সীমিত থাকে। যদিও অ্যান্ড্রয়েড রানটাইম (ART) এবং ডালভিক ভার্চুয়াল মেশিন উভয়ই নিয়মিত গার্বেজ কালেকশন করে, এর মানে এই নয় যে আপনার অ্যাপ কখন এবং কোথায় মেমরি অ্যালোকেট ও রিলিজ করে তা আপনি উপেক্ষা করতে পারেন। আপনাকে মেমরি লিক তৈরি করা থেকে বিরত থাকতে হবে—যা সাধারণত স্ট্যাটিক মেম্বার ভেরিয়েবলে অবজেক্ট রেফারেন্স ধরে রাখার কারণে ঘটে—এবং লাইফসাইকেল কলব্যাক দ্বারা সংজ্ঞায়িত সঠিক সময়ে যেকোনো Reference অবজেক্ট রিলিজ করতে হবে।

আপনার অ্যাপের কোড এবং রিসোর্স ফুটপ্রিন্ট হ্রাস করুন।

আপনার কোডের মধ্যে থাকা কিছু রিসোর্স এবং লাইব্রেরি আপনার অজান্তেই মেমরি ব্যবহার করতে পারে। থার্ড-পার্টি লাইব্রেরি বা এমবেডেড রিসোর্স সহ আপনার অ্যাপের সামগ্রিক আকার, অ্যাপটি কতটা মেমরি ব্যবহার করবে তা প্রভাবিত করতে পারে। আপনার কোড থেকে অপ্রয়োজনীয়, অতিরিক্ত বা বড় আকারের কম্পোনেন্ট, রিসোর্স এবং লাইব্রেরিগুলো সরিয়ে দিয়ে আপনি আপনার অ্যাপের মেমরি ব্যবহার উন্নত করতে পারেন।

R8 সক্রিয় করে অ্যাপের সামগ্রিক আকার হ্রাস করুন

আপনার কম্পাইল করা অ্যাপ্লিকেশন কোড আপনার রানটাইম মেমরি ফুটপ্রিন্টের একটি সক্রিয় অংশ। রান করার সময় প্রতিটি ক্লাস, মেথড, লাইব্রেরি ডিপেন্ডেন্সি এবং স্ট্রিং কনস্ট্যান্ট অবশ্যই র‍্যামে লোড করতে হয়। আপনার কম্পাইল করা কোডবেস যত বড় হবে, আপনার অ্যাপটি চালু থাকার জন্য তত বেশি ফিজিক্যাল র‍্যামের প্রয়োজন হবে।

আপনি আপনার অ্যাপের মেমরি ফুটপ্রিন্ট কমাতে R8 ব্যবহার করতে পারেন। যদিও R8 ঐতিহ্যগতভাবে APK-এর আকার কমানোর জন্য পরিচিত, তবে রানটাইম মেমরি (RAM)-এর উপর এর একটি সরাসরি ও ইতিবাচক প্রভাব রয়েছে। R8 আপনার অ্যাপের বাইটকোড বিশ্লেষণ করে অপ্রয়োজনীয় কোড বাদ দেয়, অতিরিক্ত ক্লাস একত্রিত করে, মেথড ইনলাইন করে এবং আইডেন্টিফায়ার মিনিফাই করে। APK থেকে কম কম্পাইল করা বাইটকোড RAM-এ লোড করার মাধ্যমে, এটি অ্যাপের সামগ্রিক বেসলাইন মেমরি ফুটপ্রিন্ট কমিয়ে দেয়। এছাড়াও, ক্লাস, মেথড এবং ফিল্ডের নামগুলোকে ছোট আইডেন্টিফায়ারে মিনিফাই করা সরাসরি RAM ওভারহেড কমিয়ে দেয়। ক্লাস মার্জিং এবং ব্যাপক মেথড ইনলাইনিং-এর মতো অপটিমাইজেশনগুলো ব্যয়বহুল রানটাইম লুকআপ এবং অ্যালোকেশন প্যাটার্নগুলোকেও প্রতিস্থাপন করে, যার ফলে হিপ এবং স্ট্যাক মেমরি অপটিমাইজড হয়।

নিয়মকানুন বুঝুন এবং মেনে চলুন।

Keep rules হলো কনফিগারেশন নির্দেশাবলী যা R8-কে বলে দেয় অপটিমাইজেশনের সময় আপনার কোডের কোন অংশগুলো সংরক্ষণ করতে হবে, এবং এর ফলে এটি আপনার অ্যাপের উপর নির্ভরশীল কোড মুছে ফেলা বা মিনিফাই করা থেকে বিরত থাকে। আরও তথ্যের জন্য, Keep rules overview দেখুন।

ত্রুটিপূর্ণভাবে লেখা কিপ রুলগুলো R8-কে আপনার কোডবেসের বড় অংশ অপ্টিমাইজ করতে বাধা দেয়। অতিরিক্ত ব্যাপক কিপ রুল পরিহার করুন এবং এই সর্বোত্তম অনুশীলনগুলো অনুসরণ করুন:

  • যেসব বৈশ্বিক নিয়ম এড়িয়ে চলতে হবে:
    • -dontoptimize : সম্পূর্ণ অ্যাপের জন্য অপটিমাইজেশন পুরোপুরি নিষ্ক্রিয় করে দেয়, যার ফলে এক্সিকিউটেবল ফাইলগুলো আকারে বড় ও ধীরগতির হয়।
    • -dontshrink : অব্যবহৃত কোড এবং রিসোর্স অপসারণ প্রতিরোধ করে।
    • -dontobfuscate : নাম সংক্ষিপ্তকরণ প্রতিরোধ করে, ফলে মূল্যবান মেমরি সাশ্রয়ের সুযোগ হাতছাড়া হয় (বিশেষ করে বড় অ্যাপের ক্ষেত্রে)।
  • প্যাকেজ-ব্যাপী ওয়াইল্ডকার্ড ব্যবহার পরিহার করুন: -keep class com.example.package.** { *; } এর মতো ব্যাপক নিয়মগুলো R8-কে সেই প্যাকেজের প্রতিটি ক্লাস, ফিল্ড এবং মেথড সংরক্ষণ করতে বাধ্য করে। এর ফলে সেই প্যাকেজের কোড অপসারণ, অপ্টিমাইজ বা মিনিফাই করার ক্ষেত্রে R8-এর ক্ষমতা সম্পূর্ণরূপে বন্ধ হয়ে যায়।

  • ডিফল্ট R8 কনফিগারেশন ফাইলটি ব্যবহার করুন: সর্বদা proguard-android-optimize.txt ব্যবহার করুন।

কিপ রুল লেখার বিষয়ে আরও তথ্যের জন্য, কিপ রুলস ওভারভিউ দেখুন। কোন নির্দিষ্ট প্যাটার্ন ব্যবহার করতে হবে এবং কোনটি এড়িয়ে চলতে হবে, তা জানতে কিপ রুলস বেস্ট প্র্যাকটিসেস দেখুন।

R8 কনফিগারেশন অ্যানালাইজার আপনার R8 কনফিগারেশন এবং প্রতিটি কিপ রুল কীভাবে আপনার অ্যাপকে প্রভাবিত করে, সে সম্পর্কে ধারণা দেয়। অপটিমাইজেশনকে বাধা দেয় এমন রুলগুলো কীভাবে শনাক্ত করতে হয়, সে সম্পর্কে আরও তথ্যের জন্য R8 কনফিগারেশন অ্যানালাইজার দেখুন।

বাহ্যিক লাইব্রেরি ব্যবহারে সতর্ক থাকুন।

এক্সটার্নাল লাইব্রেরির কোড প্রায়শই মোবাইল পরিবেশের জন্য লেখা হয় না এবং মোবাইল ক্লায়েন্টে কাজ করার জন্য তা অদক্ষ হতে পারে। যখন আপনি কোনো এক্সটার্নাল লাইব্রেরি ব্যবহার করেন, তখন মোবাইল ডিভাইসের জন্য সেটিকে অপটিমাইজ করার প্রয়োজন হতে পারে। এই কাজের জন্য আগে থেকেই পরিকল্পনা করুন এবং ব্যবহারের পূর্বে লাইব্রেরিটির কোডের আকার ও র‍্যাম ব্যবহারের পরিমাণ বিশ্লেষণ করুন।

এমনকি কিছু মোবাইল-অপ্টিমাইজড লাইব্রেরিও ভিন্ন ভিন্ন ইমপ্লিমেন্টেশনের কারণে সমস্যা তৈরি করতে পারে। উদাহরণস্বরূপ, একটি লাইব্রেরি লাইট প্রোটোবাফ ব্যবহার করতে পারে, যেখানে অন্যটি মাইক্রো প্রোটোবাফ ব্যবহার করে, যার ফলে আপনার অ্যাপে দুটি ভিন্ন প্রোটোবাফ ইমপ্লিমেন্টেশন দেখা যায়। লগিং, অ্যানালিটিক্স, ইমেজ-লোডিং ফ্রেমওয়ার্ক, ক্যাশিং এবং আরও অনেক অপ্রত্যাশিত জিনিসের ভিন্ন ভিন্ন ইমপ্লিমেন্টেশনের ক্ষেত্রেও এমনটা ঘটতে পারে।

R8 ব্যবহার করে আপনার অ্যাপ অপ্টিমাইজ করার মাধ্যমে ডিপেন্ডেন্সি থেকে অব্যবহৃত কোড বাদ দেওয়া গেলেও, এর কার্যকারিতা প্রায়শই লাইব্রেরির অভ্যন্তরীণ কনফিগারেশন দ্বারা সীমিত থাকে। উদাহরণস্বরূপ, কোনো লাইব্রেরির মধ্যে ব্যাপক ‘কিপ’ নিয়ম বা রিফ্লেকশনের ব্যবহার R8-কে তার কোড সংকুচিত করতে বাধা দিতে পারে, যার ফলে মেমোরি ফুটপ্রিন্ট বেড়ে যায়। কার্যকর লাইব্রেরি নির্বাচনের কৌশল জানতে, ‘লাইব্রেরি বিচক্ষণতার সাথে নির্বাচন করুন’ দেখুন।

কয়েক ডজন ফিচারের মধ্যে মাত্র এক বা দুটির জন্য শেয়ার্ড লাইব্রেরি ব্যবহার করা থেকে বিরত থাকুন। এমন বিপুল পরিমাণ কোড এবং অতিরিক্ত বোঝা যুক্ত করবেন না যা আপনি ব্যবহার করেন না। লাইব্রেরি ব্যবহার করবেন কিনা তা বিবেচনা করার সময়, এমন একটি ইমপ্লিমেন্টেশন খুঁজুন যা আপনার প্রয়োজনের সাথে ভালোভাবে মেলে। অন্যথায়, আপনি নিজের ইমপ্লিমেন্টেশন তৈরি করার সিদ্ধান্ত নিতে পারেন।

ডিপেন্ডেন্সি ইনজেকশনের জন্য Hilt বা Dagger 2 ব্যবহার করুন।

ডিপেন্ডেন্সি ইনজেকশন ফ্রেমওয়ার্কগুলো আপনার লেখা কোডকে সরল করতে পারে এবং একটি অভিযোজনযোগ্য পরিবেশ প্রদান করতে পারে, যা টেস্টিং এবং অন্যান্য কনফিগারেশন পরিবর্তনের জন্য উপযোগী।

আপনি যদি আপনার অ্যাপে কোনো ডিপেন্ডেন্সি ইনজেকশন ফ্রেমওয়ার্ক ব্যবহার করতে চান, তবে Hilt বা Dagger ব্যবহার করার কথা ভাবতে পারেন। Hilt হলো অ্যান্ড্রয়েডের জন্য একটি ডিপেন্ডেন্সি ইনজেকশন লাইব্রেরি যা Dagger-এর উপরে চলে। Dagger আপনার অ্যাপের কোড স্ক্যান করার জন্য রিফ্লেকশন ব্যবহার করে না। আপনি অপ্রয়োজনীয় রানটাইম খরচ বা মেমরি ব্যবহার ছাড়াই অ্যান্ড্রয়েড অ্যাপে Dagger-এর স্ট্যাটিক কম্পাইল-টাইম ইমপ্লিমেন্টেশন ব্যবহার করতে পারেন।

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

ডিপেন্ডেন্সি ইনজেকশন ব্যবহার করার সময়, অবজেক্টগুলোর স্কোপ যথাযথ কিনা তা নিশ্চিত করে মেমরি লিক এড়াতে সতর্ক থাকুন। ভুল লাইফসাইকেলে বাইন্ড করে অবজেক্টগুলোকে প্রয়োজনের চেয়ে বেশি সময় ধরে রাখলে মেমরি লিক হতে পারে। আরও তথ্যের জন্য, ‘স্কোপড অবজেক্টের মাধ্যমে মেমরি লিক এড়ানো’ বিষয়ক নির্দেশিকাটি দেখুন।

ছবি লোড করার ক্ষেত্রে সচেতন হোন।

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

উদাহরণস্বরূপ, বেশিরভাগ বিটম্যাপ ARGB_8888 কনফিগারেশন ব্যবহার করে, যার অর্থ প্রতিটি পিক্সেলের জন্য ৪ বাইট মেমরির প্রয়োজন হয়—লাল, সবুজ, নীল এবং আলফা (স্বচ্ছতা)-র জন্য এক বাইট করে। যদি আপনার কাছে একটি ১০০ কিলোবাইটের JPEG ফাইল থাকে এবং আপনি এটিকে ১০০০×১০০০ পিক্সেলের ভিউতে প্রদর্শন করেন, তাহলে সেই ১০,০০,০০০ পিক্সেলের প্রতিটির জন্য বিটম্যাপটির ৪ বাইট করে মেমরির প্রয়োজন হবে, যা মোট ৪ মেগাবাইট মেমরি ব্যবহার করবে।

আপনার ইমেজের ব্যবহার অপ্টিমাইজ করার জন্য বেশ কিছু উপায় রয়েছে। উদাহরণস্বরূপ, ইমেজ লোডিং লাইব্রেরি ব্যবহার করে আপনি অপ্রয়োজনীয় মেমোরি মুক্ত করতে পারেন। কীভাবে দক্ষতার সাথে ইমেজ পরিচালনা করা যায়, সে সম্পর্কে জানতে ‘Optimizing bitmap images’ দেখুন।

উপলব্ধ মেমরি এবং মেমরি ব্যবহার নিরীক্ষণ করুন

আপনার অ্যাপের মেমরি ব্যবহারের সমস্যাগুলো সমাধান করার আগে আপনাকে অবশ্যই সেগুলো খুঁজে বের করতে হবে। অ্যান্ড্রয়েড স্টুডিও মেমরি প্রোফাইলার আপনাকে নিম্নলিখিত উপায়ে মেমরির সমস্যা খুঁজে বের করতে এবং নির্ণয় করতে সাহায্য করে:

মেমরি প্রোফাইলারটি LeakCanary লিক ডিটেকশন লাইব্রেরির সাথেও সংযুক্ত। LeakCanary ব্যবহার করে, আপনি মেমরি লিক বিশ্লেষণের কাজটি টেস্ট ডিভাইস থেকে আপনার ডেভেলপমেন্ট মেশিনে স্থানান্তর করতে পারেন, যা আপনার কাজের গতি উল্লেখযোগ্যভাবে বাড়িয়ে দিতে পারে। আরও তথ্যের জন্য, অ্যান্ড্রয়েড স্টুডিও রিলিজ নোট দেখুন।

আপনার প্রোডাকশন অ্যাপ চালানো ব্যবহারকারীদের ডেটার উপর ভিত্তি করে মেমরি সমস্যা নির্ণয় করার জন্য আপনি আরও কিছু টুল ব্যবহার করতে পারেন:

ঘটনাগুলির প্রতিক্রিয়ায় স্মৃতি মুক্ত করুন

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

আপনার onTrimMemory() ফাংশনের বাস্তবায়নে শুধুমাত্র TRIM_MEMORY_UI_HIDDEN এবং TRIM_MEMORY_BACKGROUND ইভেন্টগুলোর উপরই মনোযোগ দেওয়া উচিত। (অ্যান্ড্রয়েড ১৪ থেকে, সিস্টেম অন্যান্য পুরোনো কনস্ট্যান্টগুলোর জন্য আর নোটিফিকেশন পাঠায় না। অ্যান্ড্রয়েড ১৫-এ এই কনস্ট্যান্টগুলোকে আনুষ্ঠানিকভাবে বাতিল ঘোষণা করা হয়েছে।)

  • TRIM_MEMORY_UI_HIDDEN : এই সিগন্যালটি নির্দেশ করে যে আপনার অ্যাপের UI ব্যবহারকারীর দৃষ্টির বাইরে চলে গেছে। এই পরিবর্তনটি UI-এর সাথে কঠোরভাবে যুক্ত থাকা উল্লেখযোগ্য মেমরি বরাদ্দ, যেমন বিটম্যাপ, ভিডিও প্লেব্যাক বাফার বা জটিল অ্যানিমেশন রিসোর্স, মুক্ত করার একটি সুযোগ প্রদান করে।

  • TRIM_MEMORY_BACKGROUND : এই সিগন্যালটি নির্দেশ করে যে আপনার প্রসেসটি ব্যাকগ্রাউন্ডে অবস্থান করছে এবং সিস্টেমের সার্বিক মেমরির চাহিদা মেটাতে এটিকে বন্ধ করে দেওয়ার কথা ভাবা হচ্ছে। আপনার প্রসেসটি ক্যাশড অবস্থায় বেশিক্ষণ থাকার সময়কাল বাড়াতে এবং অ্যাপ কোল্ড স্টার্টের সংখ্যা কমাতে, ব্যবহারকারী তার সেশন পুনরায় শুরু করার পর যে রিসোর্সগুলো সহজেই পুনর্গঠন করা যায়, সেগুলো দ্রুত রিলিজ করে দেওয়া উচিত।

এই কোড নমুনাটি দেখায় কিভাবে বিভিন্ন মেমরি-সম্পর্কিত ইভেন্টের প্রতিক্রিয়া জানাতে onTrimMemory() কলব্যাকটি প্রয়োগ করতে হয়:

কোটলিন

import android.content.ComponentCallbacks2
// Other import statements.

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {

    // Other activity code.

    /**
     * Release memory when the UI becomes hidden or when system resources become low.
     * @param level the memory-related event that is raised.
     */
    override fun onTrimMemory(level: Int) {

        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // Release memory related to UI elements, such as bitmap caches.
        }

        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            // Release memory related to background processing, such as by
            // closing a database connection.
        }
    }
}

জাভা

import android.content.ComponentCallbacks2;
// Other import statements.

public class MainActivity extends AppCompatActivity
    implements ComponentCallbacks2 {

    // Other activity code.

    /**
     * Release memory when the UI becomes hidden or when system resources become low.
     * @param level the memory-related event that is raised.
     */
    public void onTrimMemory(int level) {

        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // Release memory related to UI elements, such as bitmap caches.
        }

        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            // Release memory related to background processing, such as by
            // closing a database connection.
        }
    }
}

আপনার কতটা মেমরি প্রয়োজন তা পরীক্ষা করুন

একাধিক প্রসেস একসাথে চালানোর অনুমতি দেওয়ার জন্য, অ্যান্ড্রয়েড প্রতিটি অ্যাপের জন্য বরাদ্দ করা হিপ সাইজের উপর একটি নির্দিষ্ট সীমা নির্ধারণ করে দেয়। ডিভাইসে মোট কী পরিমাণ র‍্যাম উপলব্ধ আছে, তার উপর ভিত্তি করে হিপ সাইজের সঠিক সীমা ডিভাইসভেদে ভিন্ন হয়। যদি আপনার অ্যাপ হিপের ধারণক্ষমতায় পৌঁছে যায় এবং আরও মেমরি বরাদ্দ করার চেষ্টা করে, তাহলে সিস্টেম একটি OutOfMemoryError দেখায়।

মেমোরি শেষ হয়ে যাওয়া এড়াতে, বর্তমান ডিভাইসে কী পরিমাণ হিপ স্পেস উপলব্ধ আছে তা জানার জন্য আপনি সিস্টেমকে জিজ্ঞাসা করতে পারেন। আপনি getMemoryInfo() কল করে এই তথ্যটি সিস্টেম থেকে জানতে পারেন। এটি একটি ActivityManager.MemoryInfo অবজেক্ট রিটার্ন করে, যা ডিভাইসের বর্তমান মেমোরির অবস্থা সম্পর্কে তথ্য দেয়, যার মধ্যে রয়েছে উপলব্ধ মেমোরি, মোট মেমোরি এবং মেমোরি থ্রেশহোল্ড—অর্থাৎ সেই মেমোরি লেভেল যেখানে সিস্টেম প্রসেস বন্ধ করা শুরু করে। ActivityManager.MemoryInfo অবজেক্টটি lowMemory ও প্রকাশ করে, যা একটি সাধারণ বুলিয়ান ভ্যালু এবং এটি আপনাকে জানায় যে ডিভাইসে মেমোরি কমে আসছে কিনা।

নিম্নলিখিত উদাহরণ কোড স্নিপেটটি দেখায় কিভাবে আপনার অ্যাপে getMemoryInfo() মেথডটি ব্যবহার করতে হয়।

কোটলিন

fun doSomethingMemoryIntensive() {

    // Before doing something that requires a lot of memory,
    // check whether the device is in a low memory state.
    if (!getAvailableMemory().lowMemory) {
        // Do memory intensive work.
    }
}

// Get a MemoryInfo object for the device's current memory status.
private fun getAvailableMemory(): ActivityManager.MemoryInfo {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    return ActivityManager.MemoryInfo().also { memoryInfo ->
        activityManager.getMemoryInfo(memoryInfo)
    }
}

জাভা

public void doSomethingMemoryIntensive() {

    // Before doing something that requires a lot of memory,
    // check whether the device is in a low memory state.
    ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();

    if (!memoryInfo.lowMemory) {
        // Do memory intensive work.
    }
}

// Get a MemoryInfo object for the device's current memory status.
private ActivityManager.MemoryInfo getAvailableMemory() {
    ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
    activityManager.getMemoryInfo(memoryInfo);
    return memoryInfo;
}

মনিটরের কম মেমরি মারাত্মক ক্ষতি করে

যখন সিস্টেম মেমরি অত্যন্ত কমে যায়, তখন ইউজার-ভিজিবল লো মেমরি কিল (LMK) ঘটে। মেমরি কম থাকলে, lmkd (লো মেমরি কিলার ডেমন) প্রসেসগুলোকে তাদের oom_adj_score উপর ভিত্তি করে বন্ধ করে দেয়। যে অ্যাপগুলো ক্যাশ করা থাকে, অথবা এমন কোনো সার্ভিস চালায় যার সাথে কোনো ইউজার ইন্টারফেস (UI) যুক্ত নেই (যেমন একটি জব), সেগুলোর স্কোর সবচেয়ে বেশি থাকে এবং সেগুলোকে প্রথমে বন্ধ করা হয়। যদি মেমরি অত্যন্ত কম থাকে, তাহলে ডেমনটি oom_adj_score ০ থাকা প্রসেসগুলো থেকে মেমরি পুনরুদ্ধার করতে বাধ্য হয়। যেহেতু এই স্কোরটি শুধুমাত্র দৃশ্যমান অ্যাপগুলোর জন্য সংরক্ষিত থাকে, তাই সেগুলোকে বন্ধ করার ফলে প্রসেসটি তাৎক্ষণিকভাবে এবং কোনো স্বাভাবিক প্রক্রিয়া ছাড়াই বন্ধ হয়ে যায়। ব্যবহারকারীর কাছে মনে হয় যেন অ্যাপটি ক্র্যাশ করেছে, যা প্রায়শই স্ট্যান্ডার্ড লাইফসাইকেল স্টেট-সেভিং মেকানিজমগুলোকে বাইপাস করে এবং এর ফলে ব্যবহারকারীর কাজের অগ্রগতি হারিয়ে যায়।

অ্যান্ড্রয়েড ভাইটালস-এ ফোরগ্রাউন্ড প্রসেস বন্ধ করা একটি প্রধান বিবেচ্য বিষয়, কারণ এটি মেমোরি ব্যবস্থাপনার ত্রুটি নির্ণয়ের একটি নির্ভরযোগ্য সূচক হিসেবে কাজ করে। যদিও ১%-এর বেশি যেকোনো LMK রেট তাৎক্ষণিক ব্যবস্থা গ্রহণের গুরুতর প্রয়োজনীয়তা নির্দেশ করে, তবে কম রেট মানেই যে ডিভাইসের স্বাস্থ্য ভালো, এমনটা নয়। ব্যবহারকারীর কাছে কম LMK রেটের অর্থ হতে পারে যে, LMK ডেমন প্রায়শই ব্যাকগ্রাউন্ডে থাকা প্রসেসগুলোকে বন্ধ করে দিচ্ছে, যা "ওয়ার্ম স্টার্ট" পারফরম্যান্স এবং মাল্টিটাস্কিংয়ের সাবলীলতাকে ব্যাহত করে। তাই, দীর্ঘমেয়াদী স্থিতিশীলতা এবং ডিভাইসের স্বাস্থ্য নিশ্চিত করতে, আমরা আপনার বর্তমান LMK স্কোর নির্বিশেষে মেমোরি সংক্রান্ত সর্বোত্তম অনুশীলনগুলো মেনে চলার পরামর্শ দিই।

মেমরি সমস্যা ট্র্যাক করতে ProfilingManager ব্যবহার করুন

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

অ্যান্ড্রয়েড ১৭-এর সাথে যুক্ত হওয়া দুটি নতুন ট্রিগার মেমরির সমস্যা শনাক্ত করার জন্য বিশেষভাবে উপযোগী:

  • TRIGGER_TYPE_OOM নির্দেশ করে যে অ্যাপটি একটি OutOfMemoryError থ্রো করেছে। ক্র্যাশের পর অ্যাপটি পরবর্তীবার চালু হওয়ার সময়, যখন অ্যাপটি প্রোফাইলিং ট্রিগারের জন্য রেজিস্টার করে, তখন এটি ট্রিগার হয়।
  • যখন সিস্টেম অ্যাপটির কোনো অস্বাভাবিক আচরণ শনাক্ত করে, তখন TRIGGER_TYPE_ANOMALY ট্রিগার হয়। অন্যান্য কারণের মধ্যে, অতিরিক্ত মেমরি ব্যবহারের কারণেও এটি ট্রিগার হতে পারে। অ্যাপটি অতিরিক্ত মেমরি ব্যবহার করার পর এবং সমস্যা সৃষ্টিকারী প্রসেসটিকে বন্ধ করার জন্য সিস্টেম কোনো পদক্ষেপ নেওয়ার আগেই এটি ট্রিগার হয়। উদাহরণস্বরূপ, যদি অ্যাপটি অ্যান্ড্রয়েড ১৭-এ প্রবর্তিত মেমরি সীমা অতিক্রম করে, তাহলে সিস্টেম অ্যাপটিকে বন্ধ করার আগেই TRIGGER_TYPE_ANOMALY ট্রিগার হয়।

প্রোগ্রাম্যাটিকভাবে ট্রিগার নিবন্ধন এবং পুনরুদ্ধার করতে ProfilingManager ব্যবহারের বিষয়ে আরও তথ্যের জন্য, ট্রিগার-ভিত্তিক প্রোফাইলিং ডকুমেন্টেশন দেখুন।

আপনি অ্যাপ-চালিত প্রোফাইলিং ব্যবহার করে ম্যানুয়ালি ট্রেস পয়েন্টের শুরু এবং শেষ নির্ধারণ করতে পারেন। যেসব জায়গায় মেমরি লিক বা অতিরিক্ত মেমরি ব্যবহারের সন্দেহ রয়েছে, সেখানে ম্যানুয়ালি হিপ ডাম্প বা হিপ প্রোফাইল ক্যাপচার করার জন্য আমরা এটি করার পরামর্শ দিই।

আরও মেমরি-সাশ্রয়ী কোড কাঠামো ব্যবহার করুন

কিছু অ্যান্ড্রয়েড ফিচার, জাভা ক্লাস এবং কোড কনস্ট্রাক্ট অন্যগুলোর তুলনায় বেশি মেমরি ব্যবহার করে। আপনার কোডে আরও কার্যকর বিকল্প বেছে নেওয়ার মাধ্যমে আপনি আপনার অ্যাপের মেমরি ব্যবহার কমাতে পারেন।

পরিষেবাগুলি মিতব্যয়ীভাবে ব্যবহার করুন

আমরা আপনাকে জোরালোভাবে পরামর্শ দিচ্ছি যে, অপ্রয়োজনে কোনো সার্ভিস চালু রাখবেন না। অপ্রয়োজনীয় সার্ভিস চালু রাখা একটি অ্যান্ড্রয়েড অ্যাপের জন্য সবচেয়ে মারাত্মক মেমোরি-ম্যানেজমেন্ট ভুলগুলোর মধ্যে অন্যতম। যদি আপনার অ্যাপের ব্যাকগ্রাউন্ডে কাজ করার জন্য কোনো সার্ভিসের প্রয়োজন হয়, তবে কোনো কাজ সম্পন্ন করার প্রয়োজন না হলে সেটিকে চালু রাখবেন না। সার্ভিসটির কাজ শেষ হলে তা বন্ধ করে দিন। অন্যথায়, আপনার কারণে মেমোরি লিক হতে পারে।

যখন আপনি কোনো সার্ভিস চালু করেন, তখন সিস্টেম সেই সার্ভিসের প্রসেসটিকে চালু রাখতে পছন্দ করে। এই আচরণের কারণে সার্ভিস প্রসেসগুলো খুব ব্যয়বহুল হয়ে ওঠে, কারণ একটি সার্ভিস দ্বারা ব্যবহৃত র‍্যাম অন্যান্য প্রসেসের জন্য অনুপলব্ধ থাকে। এর ফলে LRU ক্যাশে সিস্টেম যে সংখ্যক ক্যাশড প্রসেস রাখতে পারে, তা কমে যায়, যা অ্যাপ সুইচিংকে কম কার্যকর করে তোলে। এমনকি, যখন মেমোরির অভাব দেখা দেয় এবং সিস্টেম বর্তমানে চলমান সমস্ত সার্ভিস হোস্ট করার জন্য পর্যাপ্ত প্রসেস বজায় রাখতে পারে না, তখন এটি সিস্টেমে থ্র্যাশিংয়ের কারণও হতে পারে।

সাধারণত, পারসিস্টেন্ট সার্ভিস ব্যবহার করা এড়িয়ে চলুন, কারণ এগুলো উপলব্ধ মেমরির উপর ক্রমাগত চাপ সৃষ্টি করে। এর পরিবর্তে, আমরা আপনাকে WorkManager মতো একটি বিকল্প ইমপ্লিমেন্টেশন ব্যবহার করার পরামর্শ দিই। WorkManager ব্যবহার করে কীভাবে ব্যাকগ্রাউন্ড প্রসেস শিডিউল করতে হয় সে সম্পর্কে আরও তথ্যের জন্য, পারসিস্টেন্ট ওয়ার্ক দেখুন।

অপ্টিমাইজ করা ডেটা কন্টেইনার ব্যবহার করুন

প্রোগ্রামিং ল্যাঙ্গুয়েজ দ্বারা প্রদত্ত কিছু ক্লাস মোবাইল ডিভাইসে ব্যবহারের জন্য অপ্টিমাইজ করা নয়। উদাহরণস্বরূপ, জেনেরিক HashMap ইমপ্লিমেন্টেশনটি মেমরির দিক থেকে অদক্ষ হতে পারে, কারণ প্রতিটি ম্যাপিংয়ের জন্য এটির একটি পৃথক এন্ট্রি অবজেক্ট প্রয়োজন হয়।

অ্যান্ড্রয়েড ফ্রেমওয়ার্কে SparseArray , SparseBooleanArray , এবং LongSparseArray সহ বেশ কিছু অপ্টিমাইজ করা ডেটা কন্টেইনার রয়েছে। উদাহরণস্বরূপ, SparseArray ক্লাসগুলো আরও বেশি কার্যকর, কারণ এগুলো সিস্টেমের key এবং কখনও কখনও value-কে অটোবক্স করার প্রয়োজনীয়তা এড়িয়ে চলে, যা প্রতিটি এন্ট্রির জন্য আরও এক বা দুটি অবজেক্ট তৈরি করে।

প্রয়োজনে, একটি সংক্ষিপ্ত ডেটা কাঠামোর জন্য আপনি র অ্যারে ব্যবহার করতে পারেন।

কোড অ্যাবস্ট্রাকশন নিয়ে সতর্ক থাকুন

ডেভেলপাররা প্রায়শই একটি ভালো প্রোগ্রামিং অনুশীলন হিসেবে অ্যাবস্ট্রাকশন ব্যবহার করেন, কারণ এটি কোডের নমনীয়তা এবং রক্ষণাবেক্ষণ উন্নত করতে পারে। তবে, অ্যাবস্ট্রাকশন কার্যকর করার জন্য সাধারণত আরও বেশি কোডের প্রয়োজন হয়। "আপনার অ্যাপের কোড এবং রিসোর্স ফুটপ্রিন্ট কমান" অংশে যেমন বিস্তারিতভাবে বলা হয়েছে , একটি বড় কম্পাইল করা কোডবেস সরাসরি আপনার অ্যাপের প্রয়োজনীয় ফিজিক্যাল র‍্যাম বাড়িয়ে দেয়। যদি আপনার অ্যাবস্ট্রাকশনগুলো উল্লেখযোগ্যভাবে উপকারী না হয়, তবে সেগুলো এড়িয়ে চলুন।

সিরিয়ালাইজড ডেটার জন্য লাইট প্রোটোবাফ ব্যবহার করুন

প্রোটোকল বাফার (প্রোটোবাফ) হলো গুগল দ্বারা ডিজাইন করা একটি ভাষা-নিরপেক্ষ, প্ল্যাটফর্ম-নিরপেক্ষ এবং সম্প্রসারণযোগ্য প্রক্রিয়া, যা স্ট্রাকচার্ড ডেটা সিরিয়ালাইজ করার জন্য ব্যবহৃত হয়। এটি XML-এর মতো, কিন্তু আকারে ছোট, দ্রুত এবং সরল। আপনি যদি আপনার ডেটার জন্য প্রোটোবাফ ব্যবহার করেন, তবে আপনার ক্লায়েন্ট-সাইড কোডে সর্বদা লাইট প্রোটোবাফ ব্যবহার করুন। সাধারণ প্রোটোবাফ অত্যন্ত বিশদ কোড তৈরি করে, যা র‍্যামে আপনার অ্যাপের কোড ফুটপ্রিন্ট বাড়িয়ে দেয় (দেখুন: আপনার অ্যাপের কোড ফুটপ্রিন্ট পরিচালনা এবং অপ্টিমাইজ করুন ) এবং APK ফাইলের আকার বৃদ্ধিতে ভূমিকা রাখে।

আরও তথ্যের জন্য, প্রোটোবাফ রিডমি দেখুন।

মেমরি লিক সম্পর্কে সতর্ক থাকুন

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

আরও তথ্যের জন্য, মেমরি লিকস দেখুন।

স্মৃতিশক্তির জঞ্জাল এড়িয়ে চলুন

গার্বেজ কালেকশন ইভেন্টগুলো আপনার অ্যাপের পারফরম্যান্সকে প্রভাবিত করে না। তবে, অল্প সময়ের মধ্যে ঘটা অনেকগুলো গার্বেজ কালেকশন ইভেন্ট দ্রুত ব্যাটারি শেষ করে দিতে পারে এবং গার্বেজ কালেক্টর ও অ্যাপ থ্রেডগুলোর মধ্যে প্রয়োজনীয় মিথস্ক্রিয়ার কারণে ফ্রেম সেট আপ করার সময়ও সামান্য বাড়িয়ে দিতে পারে। সিস্টেম গার্বেজ কালেকশনে যত বেশি সময় ব্যয় করে, ব্যাটারি তত দ্রুত শেষ হয়ে যায়।

প্রায়শই, মেমোরি চার্নের কারণে প্রচুর পরিমাণে গার্বেজ কালেকশন ইভেন্ট ঘটতে পারে। বাস্তবে, মেমোরি চার্ন বলতে একটি নির্দিষ্ট সময়ে বরাদ্দকৃত অস্থায়ী অবজেক্টের সংখ্যাকে বোঝায়।

উদাহরণস্বরূপ, আপনি একটি for লুপের মধ্যে একাধিক অস্থায়ী অবজেক্ট বরাদ্দ করতে পারেন। অথবা, আপনি একটি ভিউ-এর onDraw() ফাংশনের ভিতরে নতুন Paint বা Bitmap অবজেক্ট তৈরি করতে পারেন। উভয় ক্ষেত্রেই, অ্যাপটি দ্রুত এবং প্রচুর পরিমাণে অনেক অবজেক্ট তৈরি করে। এগুলি খুব দ্রুত ইয়ং জেনারেশনে উপলব্ধ সমস্ত মেমরি ব্যবহার করে ফেলতে পারে, যার ফলে একটি গার্বেজ কালেকশন ইভেন্ট ঘটতে বাধ্য হয়।

আপনার কোডের যে জায়গাগুলোতে মেমরি চর্ন বেশি, সেগুলো ঠিক করার আগে মেমরি প্রোফাইলার ব্যবহার করে খুঁজে বের করুন।

আপনার কোডের সমস্যাযুক্ত জায়গাগুলো চিহ্নিত করার পর, পারফরম্যান্সের জন্য গুরুত্বপূর্ণ অংশগুলোতে মেমোরি অ্যালোকেশনের সংখ্যা কমানোর চেষ্টা করুন। ভেতরের লুপের বাইরে মেমোরি সরিয়ে আনা অথবা সেগুলোকে ফ্যাক্টরি-ভিত্তিক অ্যালোকেশন কাঠামোতে নিয়ে যাওয়ার কথা বিবেচনা করতে পারেন।

অবজেক্ট পুল আপনার ব্যবহারের ক্ষেত্রে সুবিধাজনক কিনা, তাও আপনি মূল্যায়ন করতে পারেন। অবজেক্ট পুলের ক্ষেত্রে, কোনো অবজেক্ট ইনস্ট্যান্সকে ফেলে না রেখে, প্রয়োজন ফুরিয়ে গেলে সেটিকে একটি পুলে ছেড়ে দেওয়া হয়। পরের বার যখন সেই ধরনের কোনো অবজেক্ট ইনস্ট্যান্সের প্রয়োজন হয়, তখন সেটিকে বরাদ্দ না করে পুল থেকে সংগ্রহ করা যায়।

কোনো নির্দিষ্ট পরিস্থিতিতে একটি অবজেক্ট পুল উপযুক্ত কিনা তা নির্ধারণ করতে পারফরম্যান্স পুঙ্খানুপুঙ্খভাবে মূল্যায়ন করুন। এমন কিছু ক্ষেত্র আছে যেখানে অবজেক্ট পুল পারফরম্যান্সকে আরও খারাপ করে তুলতে পারে। যদিও পুল অ্যালোকেশন এড়িয়ে চলে, তবুও এটি অন্যান্য ওভারহেড তৈরি করে। উদাহরণস্বরূপ, পুল রক্ষণাবেক্ষণে সাধারণত সিনক্রোনাইজেশন জড়িত থাকে, যার একটি উল্লেখযোগ্য ওভারহেড রয়েছে। এছাড়াও, রিলিজের সময় মেমরি লিক এড়াতে পুল করা অবজেক্ট ইনস্ট্যান্সটি ক্লিয়ার করা এবং তারপর অ্যাকুইজিশনের সময় এটিকে ইনিশিয়ালাইজ করারও একটি উল্লেখযোগ্য ওভারহেড থাকতে পারে।

প্রয়োজনের চেয়ে বেশি অবজেক্ট ইনস্ট্যান্স পুলে আটকে রাখলে তা গার্বেজ কালেকশনের উপরও চাপ সৃষ্টি করে। যদিও অবজেক্ট পুল গার্বেজ কালেকশন ইনভোকেশনের সংখ্যা কমিয়ে দেয়, কিন্তু এর ফলে প্রতিটি ইনভোকেশনের জন্য প্রয়োজনীয় কাজের পরিমাণ বেড়ে যায়, কারণ এটি লাইভ (প্রাপ্য) বাইটের সংখ্যার সমানুপাতিক।