LeakCanary হল Android অ্যাপ্লিকেশনে স্বয়ংক্রিয় মেমরি লিক সনাক্তকরণের জন্য Square-এর একটি ওপেন-সোর্স লাইব্রেরি। এটি ডেভেলপমেন্ট প্রক্রিয়ার সাথে একীভূত হয় এবং Activity, Fragment, ViewModel এবং অন্যান্য উপাদানের জীবনচক্র রিয়েল টাইমে ট্র্যাক করে, লিক হওয়ার সাথে সাথেই সংকেত দেয়। Square Open Source-এর মতে, লাইব্রেরিটি হাজার হাজার প্রকল্পে ব্যবহৃত হয় এবং Android-এ মেমরি ডায়াগনস্টিক্সের জন্য ডি-ফ্যাক্টো স্ট্যান্ডার্ড হিসেবে বিবেচিত হয়।
মূল পয়েন্ট
LeakCanary হল Android অ্যাপ্লিকেশনে স্বয়ংক্রিয় মেমরি লিক সনাক্তকরণের জন্য Square-এর তৈরি একটি লাইব্রেরি। এটি অ্যাপ বিল্ড প্রক্রিয়ার সাথে একীভূত হয় এবং স্বয়ংক্রিয়ভাবে পর্যবেক্ষণ করে যে যে বস্তুগুলি ধ্বংস হওয়া উচিত (Activity, Fragment, View) সেগুলি মেমরিতে থাকে কিনা। লিক সনাক্ত হলে, LeakCanary একটি হিপ ডাম্প তৈরি করে এবং বস্তুটিকে ধরে রাখা রেফারেন্স চেইন বিশ্লেষণ করে।
লাইব্রেরিটি Android সম্প্রদায়ে একটি মানদণ্ডে পরিণত হয়েছে: GitHub-এর মতে, প্রকল্পটির 28 হাজারেরও বেশি স্টার রয়েছে এবং এটি Google, Uber, Airbnb এবং Facebook-এর অ্যাপ্লিকেশনে ব্যবহৃত হয়। LeakCanary দুটি প্রধান সংস্করণে উপলব্ধ: ক্লাসিক 1.x (ম্যানুয়াল কনফিগারেশন সহ) এবং আধুনিক 2.x (ContentProvider-এর মাধ্যমে স্বয়ংক্রিয় একীকরণ)। সংস্করণ 2.x-এ Application ক্লাস পরিবর্তনের প্রয়োজন নেই — সম্পূর্ণ কার্যকারিতার জন্য ডিপেন্ডেন্সিই যথেষ্ট।
LeakCanary-এর প্রধান কাজ হল সনাক্ত করা যখন কোনো বস্তু তার জীবনচক্র শেষ হওয়ার পরেও মেমরিতে বিদ্যমান থাকে। এটি স্ট্যাটিক ফিল্ড, সিঙ্গলটন, নিবন্ধিত নয় এমন কলব্যাক, অজ্ঞাতনামা ক্লাস এবং বাহ্যিক বস্তু ক্যাপচার করা ক্লোজারের মাধ্যমে লিকের জন্য সাধারণ।
মোবাইল ডিভাইসে সীমিত RAM-এর কারণে Android-এ মেমরি লিক ডেস্কটপের তুলনায় বেশি গুরুতর। প্রতিটি স্ক্রিন ট্রানজিশনে 5–10 MB-এর লিকও 30–40 মিনিট অ্যাপ ব্যবহারের পরে OutOfMemoryError-এর কারণ হতে পারে। LeakCanary ডেভেলপমেন্ট পর্যায়েই এই ধরনের সমস্যা সনাক্ত করে, প্রোডাকশনে ক্র্যাশের অপেক্ষা না করেই।
LeakCanary ফোর্সড গার্বেজ কালেকশনের সাথে দুর্বল রেফারেন্স (WeakReference) ব্যবহার করে। যখন কোনো Activity বা Fragment onDestroy কল করে, LeakCanary সেই বস্তুর উপর WeakReference তৈরি করে এবং অল্প বিলম্বে (ডিফল্টভাবে 5 সেকেন্ড) GC ট্রিগার করে। যদি GC-এর পরেও বস্তুটি WeakReference-এর মাধ্যমে অ্যাক্সেসযোগ্য হয়, তাহলে এটি একটি শক্তিশালী রেফারেন্স দ্বারা ধরা আছে — একটি লিক রেকর্ড করা হয়।
লিক সনাক্ত করার পর, LeakCanary একটি হিপ ডাম্প (মেমরি ডাম্প) তৈরি করে — HPROF ফরম্যাটে অ্যাপ্লিকেশন মেমরির সম্পূর্ণ স্ন্যাপশট। এরপর বিল্ট-ইন বিশ্লেষক (সংস্করণ 2.x-এর জন্য Shark) GC Roots থেকে লিক হওয়া বস্তু পর্যন্ত একটি পৌঁছানোর গ্রাফ তৈরি করে এবং সবচেয়ে ছোট পথ খুঁজে পায় — বস্তুটিকে মেমরিতে ধরে রাখা রেফারেন্স চেইন।
// LeakCanary সনাক্তকরণের সরলীকৃত যুক্তি
class ObjectWatcher {
private val watchedReferences = CopyOnWriteArrayList<KeyedWeakReference>()
fun watch(watchedObject: Any, description: String) {
val reference = KeyedWeakReference(watchedObject, description)
watchedReferences.add(reference)
BackgroundHandler.postDelayed({
checkForLeaks()
}, 5000)
}
private fun checkForLeaks() {
GcTrigger.runGc() // ফোর্সড GC
for (ref in watchedReferences) {
if (ref.get() != null) {
onLeakFound(ref) // বস্তু GC থেকে বেঁচে গেছে — এটি লিক
}
}
}
}
মূল বিষয় হল GcTrigger.runGc()-এর ফোর্সড কল। এটি ছাড়া, প্রকৃতপক্ষে লিক হওয়া বস্তু এবং GC এখনও সংগ্রহ করেনি এমন বস্তুর মধ্যে পার্থক্য করা অসম্ভব। LeakCanary এটি তিনবার পর্যন্ত করে: তিনটি GC চক্রের পরেও যদি বস্তুটি মেমরিতে থাকে, তাহলে লিক নিশ্চিত হয়।
Shark হল LeakCanary 2.x-এ নির্মিত হিপ ডাম্প বিশ্লেষক, যা Kotlin-এ লেখা। পূর্ববর্তী HAHA বিশ্লেষকের বিপরীতে, Shark পুরো HPROF ফাইল মেমরিতে লোড করে না, বরং ন্যূনতম বরাদ্দের সাথে এর বস্তু গ্রাফ ট্রাভার্স করে। এটি বিশ্লেষণের সময় RAM খরচ 50 MB থেকে 2–5 MB-এ কমিয়ে আনে এবং বিশ্লেষণের সময় 30 সেকেন্ড থেকে 1–3 সেকেন্ডে নামিয়ে আনে।
আধুনিক Android প্রকল্পে LeakCanary 2.x ইনস্টল করতে build.gradle-এ একটি লাইন লাগে। লাইব্রেরিটি স্বয়ংক্রিয় আরম্ভের জন্য ContentProvider ব্যবহার করে — Application ক্লাস পরিবর্তন করার বা MainActivity-তে কোনো কোড যোগ করার প্রয়োজন নেই। ডিপেন্ডেন্সি শুধুমাত্র debug বিল্ডের জন্য যোগ করা হয়, যাতে রিলিজ APK-তে অতিরিক্ত কোড না থাকে।
// build.gradle (app/module)
dependencies {
// debugImplementation — শুধুমাত্র debug বিল্ডের জন্য লাইব্রেরি
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}
ডিপেন্ডেন্সি যোগ এবং প্রকল্প পুনর্নির্মাণের পর, LeakCanary স্বয়ংক্রিয়ভাবে অ্যাপ্লিকেশনে উপস্থিত হয়। প্রথম লঞ্চে, লাইব্রেরিটি সক্রিয়করণ নিশ্চিত করে একটি সিস্টেম বিজ্ঞপ্তি দেখায়। সমস্ত সনাক্ত করা লিক বিজ্ঞপ্তি হিসাবে প্রদর্শিত হয় — বিজ্ঞপ্তিতে ট্যাপ করলে বিস্তারিত রিপোর্ট (LeakTrace) সহ একটি স্ক্রিন খোলে।
কাস্টমাইজেশনের জন্য, আপনি নিজের AppWatcherInstaller তৈরি করতে পারেন এবং প্যারামিটার ওভাররাইড করতে পারেন: GC টাইমআউট, ট্র্যাক করা বস্তুর প্রকারের তালিকা, ডিস্কে হিপ ডাম্প সংরক্ষণ সক্ষম করা। তবে, 90% প্রকল্পের জন্য ডিফল্ট কনফিগারেশন সর্বোত্তম।
সংস্করণ 2.12 থেকে শুরু করে, LeakCanary ViewModel, করুটিন স্কোপ এবং Compose State বস্তুর স্বয়ংক্রিয় ট্র্যাকিং সমর্থন করে। কোনো অতিরিক্ত ডিপেন্ডেন্সির প্রয়োজন নেই — লাইব্রেরিটি স্বয়ংক্রিয়ভাবে সনাক্ত করে প্রকল্পে কোন Jetpack উপাদানগুলি ব্যবহার করা হচ্ছে এবং সংশ্লিষ্ট ডিটেক্টরগুলি সক্রিয় করে।
LeakCanary রিপোর্ট (LeakTrace) হল GC Root থেকে লিক হওয়া বস্তু পর্যন্ত একটি বহু-লাইন রেফারেন্স চেইন। প্রতিটি লাইন ক্লাস এবং ফিল্ড দেখায় যার মাধ্যমে একটি শক্তিশালী রেফারেন্স যায়। ডেভেলপারদের চেইনটি নিচ থেকে উপরে পড়তে হবে: নীচের লাইনটি লিক হওয়া বস্তু, উপরের লাইনটি প্রবেশ বিন্দু (GC Root)।
একটি সাধারণ LeakTrace দেখতে এমন: GC Root → Application-এর স্ট্যাটিক ফিল্ড → সিঙ্গলটন → কলব্যাক → Activity। যদি ডেভেলপার এই ধরনের চেইন দেখেন, সমস্যাটি স্পষ্ট: সিঙ্গলটন একটি কলব্যাক ধরে রেখেছে যা Activity-র একটি রেফারেন্স ক্যাপচার করেছে। সমাধান — সিঙ্গলটনে শক্তিশালী রেফারেন্সকে দুর্বল রেফারেন্স দিয়ে প্রতিস্থাপন করা।
┬
├─ android.app.Application
│ Leaking: NO (Application — singleton)
│ ↓ Application.leakedActivities
├─ java.util.ArrayList
│ Leaking: NO (ArrayList — normal)
│ ↓ ArrayList[0]
├─ com.example.MainActivity
│ Leaking: YES (Activity destroyed but still in memory)
│ ↓ MainActivity.mCallback
├─ com.example.CallbackWrapper
│ Leaking: UNKNOWN
│ ↓ CallbackWrapper.mListener
│ ~~~~~~~~~~
├─ com.example.MyCallback (anonymous)
│ Leaking: UNKNOWN
│ ↓ MyCallback.this$0
├─ com.example.MainActivity
│ Leaking: YES (MainActivity is the leak)
╰
এই উদাহরণে, LeakCanary দেখায় যে MainActivity চেইনের মাধ্যমে ধরা আছে: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → আবার MainActivity। this$0 তীর নির্দেশ করে যে অজ্ঞাতনামা ক্লাস MyCallback Activity-র একটি বাহ্যিক রেফারেন্স ক্যাপচার করেছে। সমাধান — কলব্যাককে দুর্বল রেফারেন্স বানানো বা এটি onDestroy-তে বাতিল করা।
LeakCanary চেইনের প্রতিটি উপাদানের জন্য লিক অবস্থাও দেখায়: NO (কোনো লিক নয় — এটি একটি মূল উপাদান), YES (বস্তুটি ধ্বংস করা উচিত), UNKNOWN (অবস্থা নির্ধারণ করা যায়নি)। UNKNOWN অবস্থার অর্থ সমস্যা নয় — এটি একটি মধ্যবর্তী বস্তু যা LeakCanary নিশ্চিতভাবে শ্রেণীবদ্ধ করতে পারে না।
সংস্করণ 1.x থেকে 2.x-এ রূপান্তর ছিল মৌলিক: ডেভেলপাররা লাইব্রেরিটি প্রথম থেকে পুনরায় লিখেছেন, পুরানো HAHA বিশ্লেষককে Kotlin-এ লেখা তাদের নিজস্ব ইঞ্জিন Shark দিয়ে প্রতিস্থাপন করেছেন। Shark অনেক গুণ দ্রুত, বিশ্লেষণের জন্য কম মেমরি প্রয়োজন এবং লিকের মূল কারণগুলি আরও নির্ভুলভাবে নির্ধারণ করে।
| প্যারামিটার | LeakCanary 1.x | LeakCanary 2.x |
|---|---|---|
| বিশ্লেষকের ভাষা | Java (HAHA — Android SDK-এর ফোর্ক) | Kotlin (Shark — নিজস্ব ইঞ্জিন) |
| সেটআপ | Application-এ ম্যানুয়াল AppWatcher কনফিগারেশন | ContentProvider-এর মাধ্যমে স্বয়ংক্রিয় |
| গতি | হিপ ডাম্প বিশ্লেষণের জন্য 10–30 সেকেন্ড | হিপ ডাম্প বিশ্লেষণের জন্য 1–5 সেকেন্ড |
| কার্যকারিতা | বিশ্লেষণের সময় 10–50 MB RAM নেয় | বিশ্লেষণের সময় 2–10 MB RAM নেয় |
Shark-এর মূল সুবিধা হল এটি পুরো হিপ ডাম্প মেমরিতে লোড করে না, বরং ন্যূনতম বরাদ্দের সাথে এর রেফারেন্স গ্রাফ ট্রাভার্স করে। এটি LeakCanary 2.x-কে বিশ্লেষণের সময় OutOfMemoryError-এর ঝুঁকি ছাড়াই কম RAM-যুক্ত ডিভাইসে ব্যবহারের জন্য উপযুক্ত করে তোলে।
সংস্করণ 2.x-এ Android Studio Memory Profiler-এ পরবর্তী বিশ্লেষণের জন্য হিপ ডাম্প রপ্তানি করার ক্ষমতাও যুক্ত হয়েছে। এটি করতে, AppWatcher কনফিগারেশনে dumpHeapWhenLeakFound সেটিং সক্ষম করুন।
LeakCanary Android-এ সাধারণ লিকের বেশ কয়েকটি শ্রেণী কার্যকরভাবে সনাক্ত করে। সবচেয়ে সাধারণ হল Activity-তে স্ট্যাটিক রেফারেন্সের মাধ্যমে লিক — ডেভেলপাররা সিঙ্গলটনে Activity কন্টেক্সটের রেফারেন্স রাখে, এবং Activity তার জীবনচক্র শেষ হওয়ার পর GC দ্বারা সংগ্রহ করা যায় না।
দ্বিতীয় সবচেয়ে সাধারণ শ্রেণী হল নিবন্ধিত নয় এমন শ্রোতা-র মাধ্যমে লিক। যদি onStart-এ registerListener কল করা হয়েছিল কিন্তু onStop/onDestroy-তে unregisterListener কল করা না হয়, তাহলে শ্রোতা বস্তুটি Activity ধ্বংস হওয়ার পরেও সিস্টেম দ্বারা ধরা থাকে। LeakCanary স্পষ্টভাবে দেখায় কোন শ্রোতা এবং কোন সিস্টেম পরিষেবায় জীবিত রয়েছে।
// সাধারণ লিক: সিঙ্গলটন কলব্যাকে Activity ক্যাপচার হয়েছে
object AnalyticsManager {
private var callback: ((String) -> Unit)? = null
fun register(callback: (String) -> Unit) {
this.callback = callback // কলব্যাকে শক্তিশালী রেফারেন্স
}
fun unregister() {
callback = null // onDestroy-এ কল করতে ভুলবেন না!
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AnalyticsManager.register { event ->
logEvent(event) // ল্যাম্বডা this ক্যাপচার করে
}
// যদি onDestroy-এ unregister কল না করা হয় → Activity লিক
}
}
তৃতীয় শ্রেণী হল BackStack-এ Fragment-এর মাধ্যমে লিক। যদি FragmentTransaction.addToBackStack() পিছনে যাওয়ার সময় Fragment না সরিয়েই কল করা হয়, তাহলে Fragment-এর পুরানো ইনস্ট্যান্স মেমরিতে থাকে। LeakCanary ডেভেলপমেন্টের প্রথম দিকে এই ধরনের লুকানো লিক সনাক্ত করতে সাহায্য করে।
প্রতিটি সনাক্ত করা লিকের জন্য, LeakCanary বিবরণ এবং ঠিক করার জন্য সুপারিশ প্রদান করে। সংস্করণ 2.14-এ Android Lint-এর সাথে একীকরণ যুক্ত হয়েছে — লাইব্রেরি CI-তে লিক সনাক্ত হলে স্বয়ংক্রিয়ভাবে ইস্যু ট্র্যাকারে কাজ তৈরি করতে পারে।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
হ্যাঁ, অবশ্যই। LeakCanary build.gradle-এ debugImplementation-এর মাধ্যমে যোগ করা হয়, যা এটিকে রিলিজ বিল্ড থেকে স্বয়ংক্রিয়ভাবে বাদ দেয়। যদি implementation ব্যবহার করা হয়, তাহলে লাইব্রেরিটি রিলিজ APK-তে অন্তর্ভুক্ত হবে এবং শেষ ব্যবহারকারীদের লিক দেখাবে — এটি অগ্রহণযোগ্য।
কার্যকারিতার উপর প্রভাব ন্যূনতম। LeakCanary শুধুমাত্র উপাদানের onDestroy-এর পরে সক্রিয় হয় এবং UI রেন্ডারিং বা স্পর্শ পরিচালনায় হস্তক্ষেপ করে না। একমাত্র খরচ হল সামান্য ফোর্সড GC বিরতি (প্রায় 100 ms) এবং লিক হলে হিপ ডাম্প লেখা (সেকেন্ডের ভগ্নাংশ)।
LeakCanary স্বয়ংক্রিয়ভাবে হিপ ডাম্প HPROF ফরম্যাটে অ্যাপ্লিকেশন ফোল্ডারে সংরক্ষণ করে। ফাইলটি Android Studio-এর মাধ্যমে রপ্তানি করা যেতে পারে: Device File Explorer → data/data/com.example/files/leakcanary/. এটি দেখতে, Capture → Open Heap Dump-এর মাধ্যমে Memory Profiler-এ ফাইলটি খুলুন।
হ্যাঁ, সংস্করণ 2.12 থেকে LeakCanary সম্পূর্ণরূপে Jetpack Compose সমর্থন করে। লাইব্রেরিটি Composition কনটেক্সট এবং State বস্তু ট্র্যাক করে, Composable ফাংশনে স্বয়ংক্রিয়ভাবে লিক সনাক্ত করে। আলাদা কনফিগারেশনের প্রয়োজন নেই — এটি বক্সের বাইরে কাজ করে।
মিথ্যা পজিটিভ সম্ভব তবে বিরল। LeakCanary লিক ঘোষণা করার আগে তিনবার GC কল ব্যবহার করে, যা বেশিরভাগ মিথ্যা পজিটিভ দূর করে। যদি আপনি মনে করেন একটি সনাক্তকরণ মিথ্যা পজিটিভ, তাহলে কনফিগারেশনে নির্দিষ্ট ক্লাসের জন্য একটি IgnoredReference তৈরি করুন।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন