বিভিন্ন গারবেজ কালেকটর (ব্যাকগ্রাউন্ড প্রোগ্রাম যা মূলত গারবেজ কালেকশনের কাজ করে থাকে) বিভিন্ন পদ্ধতি বা এলগরিদম ব্যবহার করে এই ধাপগুলো বাস্তবায়ন করে থাকে। এসম্পর্কে আমরা পরে আরো আলোচনা করবো।
মেমোরি মডেল
গারবেজ কালেকশন নিয়ে জানতে হলে আমাদের প্রথমে জে.ভি.এম. এর মেমোরি মডেল সম্পর্কে জানতে হবে। জে.ভি.এম. মেমরির যে অংশে অ্যাপলিকেশনের সব অবজেক্টগুলো রাখা হয় তাকে বলে হিপ মেমরি। হিপ মেমরির বিভিন্ন অংশ সম্পর্কে জানা যাক।
জে.ভি.এম. মেমোরির বিভিন্ন অংশ
হিপ মেমরিকে মোটা দাগে দুই ভাগে ভাগ করা যায়: ১। ইয়ং জেনারেশন বা নার্সারি স্পেস এবং ২। ওল্ড জেনারেশন বা টেনিউরড স্পেস । ইয়ং জেনারেশনকে আবার ভাগ করা হয় এডেন স্পেস আর দুটো সারভাইভর স্পেসে। হিপ মেমরি ছাড়া জে.ভি.এম. মেমরিতে পার্মানেন্ট জেনারেশন নামের অংশ আছে, যেখানে অ্যাপলিকেশন মেটাডাটা (মেথড, ক্লাস সম্পর্কিত ডাটা) থাকে।
ইয়ং জেনারেশন ও মাইনর জিসি
নতুন তৈরি হওয়া অবজেক্টগুলো থাকে এডেন স্পেসে। এডেন স্পেস যখন অবজেক্টে পরিপূর্ণ হয়ে যায়, তখন একটা মাইনর গারবেজ কালেকশন (Minor GC) সম্পন্ন হয় এবং বেঁচে যাওয়া অবজেক্টেগুলো সারভাইভার স্পেসে সরিয়ে আনা হয়। একটা সারভাইভার স্পেস পরিপূর্ণ হয়ে গেলে অবজেক্টগুলো অন্য সারভাইভার স্পেসে সরিয়ে আনা হয়। এটা মনে রাখতে হবে যে, একটা সারভাইভর স্পেস সব সময়ই খালি রাখা হয়।
ফুল জিসি
সাধারণভাবে অনেক সময় মেজর জিসিকেই ফুল জিসি (Full GC) হিসেবে অভিহিত করা হয়। বিস্তারিতভাবে বলতে গেলে, ফুল জিসি সম্পন্ন হওয়া মানে হল পুরো হিপ মেমরি ক্লিন করা, অর্থাৎ ইয়ং জেনারেশন ও ওল্ড জেনারেশন দুটোই ক্লিন করা। ফুল জিসিতে পার্মানেন্ট জেনারেশনও ক্লিন করা হয়।
পারফরমেন্সের সাথে সম্পর্ক
একটা খুব গুরুত্বপূর্ণ বিষয় আমাদের মনে রাখতে হবে যে, গারবেজ কালেকশনকে "স্টপ দি ওয়ার্ল্ড" (Stop the world) ইভেন্ট বলা হয়। তার মানে হল, যখন গারবেজ কালেকশনের কাজ চলতে থাকে, তখন সকল অ্যাপ্লিকেশন থ্রেড বন্ধ থাকে। মাইনর জিসিতে সময় কম লাগে কারণ ইয়ং জেনারেশনে অবজেক্টেগুলো কম সময়ের জন্য থাকে। যেজন্য অ্যাপ্লিকেশান এর প্রভাব কম। কিন্তু মেজর জিসিতে সময় অনেক বেশি লাগে, একারণে অ্যাপ্লিকেশন বেশি সময়ের জন্য রেসপন্স করা থেকে বিরত থাকে। তাই মেজর জিসি বেশি করে হলে অ্যাপ্লিকেশনের পারফরমেন্স কমে যাবে।
হিপ মেমরি সংক্রান্ত জেভিএম অপশন
জাভা অ্যাপ্লিকেশান চালানোর সময় আমরা অনেক রকম জেভিএম অপশন (JVM option) বা আর্গুমেন্ট ব্যবহার করতে পারি। এর মধ্যে কতগুলো রয়েছে হিপ মেমরির সাথে সম্পর্কিত। গুরুত্বপূর্ণ কতগুলো হিপ মেমরি সংক্রান্ত জেভিএম অপশনগুলো হলঃ
- -Xms নির্ধারণ করে জেভিএম শুরু হবার সময় প্রাথমিক হিপ সাইজ
- -Xmx নির্ধারণ করে সর্বোচ্চ হিপ সাইজ
- -Xmn নির্ধারণ করে ইয়ং জেনারেশনের সাইজ
- -XX:PermGen নির্ধারণ করে পার্মানেন্ট জেনারেশনের প্রাথমিক সাইজ
- -XX:MaxPermGen নির্ধারণ করে পার্মানেন্ট জেনারেশনের সর্বোচ্চ সাইজ
- -XX:SurvivorRatio নির্ধারণ করে এডেন স্পেস আর সারভাইভর স্পেসের সাইজের অনুপাত
- -XX:NewRatio নির্ধারণ করে ওল্ড জেনারেশন আর ইয়ং জেনারেশনের সাইজের অনুপাত
এছাড়া গারবেজ কালেকটরের ধরন অনুযায়ী বেশ কিছু জেভিএম অপশন রয়েছে, যেগুলো গারবেজ কালেকটর এলগরিদম অংশে আলোচনা করা হয়েছে।
কমান্ড লাইনে রান করার জন্য java কমান্ডের পর আর্গুমেন্টগুলো একটার পর একটা দিয়ে দিলে হবে। যেমনঃ
java -Xms128m -Xmx256m ...
আইডিই যেমন একলিপসে রান করতে চাইলে “Run Configurations” এ গিয়ে “Arguments” ট্যাবে ভিএম আর্গুমেন্ট দিয়ে দিতে হবে:
অ্যাপ্লিকেশান সার্ভার যেমন টমক্যাটের কনফিগারেশনেও জেভিএম আর্গুমেন্ট দেয়া যায়। টমক্যাটের bin ডিরেক্টোরির catalina.sh (উইন্ডোজের ক্ষেত্রে catalina.bat) ফাইলের CATALINA_OPTS অথবা JAVA_OPTS ভেরিয়েবলে আর্গুমেন্টগুলো দিয়ে দিতে হবে। যেমনঃ
CATALINA_OPTS=-Xms128m -Xmx256m
হিপ মেমরি ও জিসি মনিটর করা
হিপ মেমরি আর গারবেজ কালেকশনের কাজকর্ম দেখার জন্য বিভিন্ন রকম টুল ব্যবহার করা যায়। আমরা এখানে কমান্ড লাইন টুল jstat এবং গ্রাফিকাল ইউজার ইনটারফেস টুল VisualVM ও Visual GC কিভাবে ব্যবহার করতে হয় সেটা দেখবো।
১। কমান্ড লাইন টুল jstat দিয়ে
কমান্ড লাইন টুল jstat আলাদাভাবে ইন্সটল করতে হয় না, জেডিকে(JDK)-এর সংগেই থাকে। jstat কমান্ড রান করার জন্য আমাদের জাভা অ্যাপ্লিকেশানের প্রসেস আইডি জানতে হবে। লিনাক্স কিংবা ম্যাকে নিচের কমান্ড দিয়ে প্রসেস আইডি জানতে হবেঃ
প্রসেস আইডি জানার পর নিচের কমান্ড দিয়ে jstat ব্যবহার করা যাবেঃ
jstat -gc [JAVA_PID] [INTERVAL_MS]
শেষ প্যারামিটার (INTERVAL_MS) দিয়ে আমরা নির্ধারণ করতে পারি কত মিলিসেকেন্ড সময় পর পর আপডেটেড ইনফর্মেশন দেখাবে। jstat কমান্ডের একটা উদাহরণঃ
উপরের কমান্ড দিয়ে আমরা অনেকটা নিচের মত আউটপুট দেখতে পারিঃ
jstat কমান্ড আউটপুটের কলামগুলোর সংক্ষিপ্ত বর্ণনাঃ
- S0C সারভাইভর স্পেস-0 এর বর্তমান ধারণক্ষমতা (কিলোবাইট).
- S1C সারভাইভর স্পেস-1 এর বর্তমান ধারণক্ষমতা (কিলোবাইট).
- S0U ব্যবহৃত সারভাইভর স্পেস-0 (কিলোবাইট).
- S1U ব্যবহৃত সারভাইভর স্পেস-1 (কিলোবাইট).
- EC এডেন স্পেসের বর্তমান ধারণক্ষমতা (কিলোবাইট).
- EU ব্যবহৃত এডেন স্পেস (কিলোবাইট).
- OC ওল্ড জেনারেশন স্পেসের বর্তমান ধারণক্ষমতা (কিলোবাইট).
- OU ব্যবহৃত ওল্ড জেনারেশন স্পেস (কিলোবাইট).
- MC মেটাস্পেসের ধারণক্ষমতা (কিলোবাইট)
- MU ব্যবহৃত মেটাস্পেস (কিলোবাইট)
- CCSC কমপ্রেসেড ক্লাস স্পেসের ধারণক্ষমতা (কিলোবাইট)
- CCSU ব্যবহৃত কমপ্রেসেড ক্লাস স্পেস (কিলোবাইট)
- YGC ইয়ং জেনারেশন গারবেজ কালেকসনের সংখ্যা
- YGCT ইয়ং জেনারেশন গারবেজ কালেকসনের সময়
- FGC ফুল জিসির সংখ্যা
- FGCT ফুল জিসির সময়
- GCT সর্বমোট গারবেজ কালেকসনের সময়
২। VisualVM ও Visual GC ব্যবহার করে
হিপ মেমোরি ও গারবেজ কালেকসনের কার্যক্রম যদি আমরা ভিস্যুয়ালি দেখতে চাই বা গ্রাফিক্যাল ইউজার ইন্টারফেস ব্যবহার করে দেখতে চাই, তাহলে VisualVM একাজের জন্য খুব ভালো একটা টুল। এটি জেডিকের (JDK) সাথে বিল্ট-ইন দেয়া থাকে। কমান্ড লাইনে নিচের ছোট কমান্ড দিয়ে VisualVM অ্যাপ্লিকেশান চালু করা যায়:
এটি চালু হবার পর বাম পাশের অ্যাপ্লিকেশানস (Applications) ট্যাবে জাভা অ্যাপ্লিকেশানগুলোর তালিকা পাওয়া যাবে, যেখান থেকে লোকাল ও রিমোট যেকোনো অ্যাপ্লিকেশান নির্বাচন করা যায়, যা আমরা মনিটর করতে পারি। ডানপাশের মনিটর (Monitor) ট্যাবে সিপিউর (CPU) ব্যবহার, মেমরির ব্যবহারের বিভিন্ন গ্রাফ দেখা যায়। নিচে মনিটর ট্যাবের একটি স্ক্রীনশটঃ
VisualVM-এর একটা দরকারি প্লাগইন হচ্ছে Visual GC। এটা ইন্সটল করার জন্য Tools মেনু থেকে Plugins অপশন সিলেক্ট করতে হবে। Plugins উইনডো থেকে Visual GC ইন্সটল করে নিতে হবে। ইন্সটল হবার পর বামপাশের লিস্ট থেকে অ্যাপ্লিকেশান সিলেক্ট করলে Visual GC নামে আরেকটা ট্যাব থাকবে যেখানে হিপ মেমোরি ও গারবেজ কালেকসনের কিছু গ্রাফ দেখা যাবে। নিচে Visual GC ট্যাবের একটি স্ক্রীনশটঃ
চাইলে কোন রিমোট সার্ভারের মেমরিও আমরা মনিটর করতে পারি। সেক্ষেত্রে JMX connection তৈরি করে আমাদের সংযুক্ত হতে হবে। এক্ষেত্রে অবশ্যই রিমোট সার্ভারের জেভিএম অপশনে jmxremote এর সাথে সম্পর্কিত প্যারামিটারগুলো থাকতে হবে। jmxremote প্যারামিটারগুলোর একটি উদাহরণ নিচে হলঃ
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9010
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
কোন কারণে অ্যাপ্লিকেশানে মেমরি লিক হলে বা OutOfMemoryError পাওয়া গেলে হিপ ডাম্প (Heap Dump) বিশ্লেষণ করে এর কারণ খুঁজে বের করা যায়। নিচের জেভিএম অপশন ব্যবহার করলে অ্যাপ্লিকেশানে যদি OutOfMemoryError হয়, তখন একটা হিপ ডাম্প ফাইল তৈরি হবেঃ
-XX:+HeapDumpOnOutOfMemoryError –XX:HeapDumpPath=[file path]
নিচে হিপ ডাম্প ট্যাবের একটি স্ক্রীনশটঃ
গারবেজ কালেকটর এলগরিদমসমূহ
মূলত চার ধরনের গারবেজ কালেকটর আছে যেগুলো ভিন্ন ভিন্ন এলগরিদম ব্যবহার করে। প্রত্যেকটারই কিছু সুবিধা, অসুবিধা আছে। অ্যাপ্লিকেশানের চাহিদা অনুসারে আমাদের বেছে নিতে হবে কোন গারবেজ কালেকটর করাটা বেশি সুবিধাজনক হবে।
১। সিরিয়াল কালেকটর
সিরিয়াল কালেকটরের কাজের পদ্ধতি সবচেয়ে সরল যা সিরিয়ালি কাজ করে বা একক থ্রেড (Single thread) ব্যবহার করে কাজ করে। এটি মার্ক-সুইপ-কমপ্যাক্ট পদ্ধতি প্রয়োগ করে গারবেজ কালেকট করে থাকে। এটি ক্লায়েন্ট মেশিন বা একক-সিপিউ (Single-CPU) মেশিনের জন্য বেশি উপযোগী। যেসব অ্যাপ্লিকেশান ১০০ মেগাবাইটের কম মেমরি ব্যবহার করে, বা খুব বেশি রেস্পন্সিভ হবার দরকার নেই, সেসব অ্যাপ্লিকেশানে সিরিয়াল কালেকটর ব্যবহার করা যায়। -XX:+UseSerialGC জেভিএম অপশন দিয়ে সিরিয়াল কালেকটর নির্বাচন করা যায়।
২। প্যারালাল বা থ্রপুট কালেকটর
প্যারালাল কালেকটর সিরিয়াল কালেকটরের মত করেই কাজ করে, পার্থক্য হল এটা ইয়ং জেনারেশন কালেকট করার জন্য একাধিক থ্রেড (Multiple thread) ব্যবহার করে। সাধারণভাবে এটা সিস্টেমের সিপিউ কোর (CPU core) সংখ্যার সমান সংখ্যক থ্রেড ব্যবহার করে। অবশ্য –XX:ParallelGCThreads=n জেভিএম অপশন দিয়ে থ্রেডের সংখ্যা নির্ধারণ করে দেয়া যায়। তবে চাইলে ওল্ড জেনারেশনের জন্যেও একাধিক থ্রেড কাজে লাগানো যায়। সেক্ষেত্রে, -XX:+UseParallelOldGC জেভিএম অপশন দিয়ে দিতে হবে। তখন ইয়ং জেনারেশন ও ওল্ড জেনারেশন উভয়ের জন্যই একাধিক থ্রেড ব্যবহার করা হবে। একাধিক সিপিউ কোর ব্যবহার করে থাকে বলে একে থ্রপুট কালেকটরও বলা হয়। সেসব অ্যাপ্লিকেশানে বড় কাজ (এই যেমন ব্যাচ জব প্রসেস করার কাজ) করতে হবে এবং দীর্ঘ বিরতি (long pause) তেমন কোন সমস্যা নয়, সেসবে প্যারালাল কালেকটর ব্যবহার করা যায়।
৩। সিএমএস কালেকটর
কনকারেন্ট মার্ক সুইপ (Concurrent Mark Sweep) বা সিএমএস কালেকটর ডিজাইন করা হয়েছে স্বল্প বিরতির (low pause) কালেকটর হিসেবে। মাইনর জিসির সময় এটা সব অ্যাপ্লিকেশান থ্রেড বন্ধ রাখে এবং একাধিক থ্রেড ব্যবহার করে কাজ করে। অর্থাৎ ইয়ং জেনারেশন কালেক্ট করার জন্য এর কাজের পদ্ধতি মোটামুটি প্যারালাল কালেকটরের মতই।
ওল্ড জেনারেশনের জন্যে সিএমএস কালেকটর ভিন্ন পদ্ধতিতে কাজ করে। এটা অ্যাপ্লিকেশান থ্রেডসমূহ বন্ধ না করেই কিছু ব্যাকগ্রাউন্ড জিসি থ্রেড দিয়ে ওল্ড জেনারেশন স্ক্যান করতে থাকে এবং অব্যবহৃত অবজেক্টগুলোকে মুছে ফেলতে থাকে। অ্যাপ্লিকেশান থ্রেডসমূহ বন্ধ করা হয় শুধু ইয়ং জেনারেশন কালেক্ট করার জন্য। একারণে বিরতির পরিমাণ অনেক কম হয়। সিএমএস কালেকটর নির্বাচন করা যায় -XX:+UseConcMarkSweepGC জেভিএম অপশন দিয়ে এবং ব্যাকগ্রাউন্ড জিসি থ্রেডের সংখ্যা নির্ধারণ করা যায় -XX:ParallelCMSThreads=n জেভিএম অপশন দিয়ে। যেসব অ্যাপ্লিকেশান খুব রেসপন্সিভ বা স্বল্প বিরতি গ্রহণযোগ্য, সেসবে সিএমএস কালেকটর ব্যবহার করা যায়।
সিএমএস কালেকটরের কিছু সীমাবদ্ধতা রয়েছে। অ্যাপ্লিকেশান থ্রেডের সংগে একসাথে চলমান ব্যাকগ্রাউন্ড জিসি থ্রেডগুলোর হিপ স্ক্যান করার কাজ করার জন্য পর্যাপ্ত পরিমাণ সিপিউ সাইকেল (CPU cycles) সিস্টেমে থাকতে হবে। আর ব্যাকগ্রাউন্ড জিসি থ্রেডগুলো মেমরি কমপ্যাক্ট করার কাজ করেনা। একারণে হিপ মেমোরি ফ্রাগমেন্টেড (fragmented) বা বিক্ষিপ্ত হয়ে যেতে পারে। যখন ব্যাকগ্রাউন্ড জিসি থ্রেডগুলো পর্যাপ্ত পরিমাণ সিপিউ সাইকেল না পায়, অথবা হিপ মেমোরি অনেক বেশি ফ্র্যাগমেন্টেড হয়ে যায়, তখন অ্যাপ্লিকেশান থ্রেডসমূহ বন্ধ করে একটা ফুল জিসি সম্পন্ন করা হয়। এইক্ষেত্রে বিরতির পরিমাণ বেশি থাকে।
৪। জি১ কালেকটর
জি১ (Garbage first) কালেকটর ডিজাইন করা হয়েছে বড় হিপ মেমরি নিয়ে কাজ করার জন্য এবং একই সাথে স্বল্প বিরতির কালেকটর হিসেবে। এটা হিপ মেমরিকে কিছু সংখ্যক সমান আকারের রিজিয়ন বা অংশে ভাগ করে কাজ করে। এরমধ্যে কয়েকটা রিজিয়ন মিলে ইয়ং জেনারেশন, কয়েকটা রিজিয়ন মিলে সারভাইভার স্পেস ও কয়েকটা রিজিইয়ন মিলে ওল্ড জেনারেশন গঠিত হয়।
জি১ কালেকটর হিপ
অন্যান্য কালেক্টরের মত সকল অ্যাপ্লিকেশান থ্রেড বন্ধ ইয়ং জেনারেশনে গারবেজ কালেক্ট করা হয়। জি১ এক প্রকার কনকারেন্ট কালেকটরও কারণ অ্যাপ্লিকেশান থ্রেডসমূহ বন্ধ না করে কিছু ব্যাকগ্রাউন্ড জিসি থ্রেড দিয়ে এটা ওল্ড জেনারেশন কালেক্ট করতে থাকে। জি১ কালেকটর ওল্ড জেনারেশনের অবজেক্টগুলো ক্লিন করার জন্য এক রিজিয়ন থেকে আরেক রিজিয়নে কপি করে। এসময় জি১ কালেকটর কমপ্যাক্ট করার কাজও করে থাকে। তাই জি১ কালেকটর ব্যবহার করলে হিপ মেমরি ফ্রাগমেন্টেড হবার সম্ভাবনা অনেক কম থাকে। -XX:+UseG1GC জেভিএম অপশন দিয়ে জি১ কালেকটর নির্বাচন করা যায়। যেসব রেসপন্সিভ অ্যাপ্লিকেশানের হিপ মেমরি সাইজ বেশি (৪ জিবির বেশি), সেসবে জি১ কালেকটর ব্যবহার করা ভালো।
সিএমএস কালেকটরের মত জি১ কালেকটরেও ব্যাকগ্রাউন্ড জিসি থ্রেডগুলোর কাজ করার জন্য পর্যাপ্ত পরিমাণ সিপিউ সাইকেল (CPU cycles) সিস্টেমে থাকতে হবে।
জিসি টিউন করা
জিসি টিউন করার জন্য কোন ধরাবাঁধা নিয়ম নেই। গারবেজ কালেকশন সম্পন্ন হবার সময় যদি অ্যাপ্লিকেশানের পারফরম্যান্সে প্রভাব ফেলে, তখন জিসি টিউন করার চেষ্টা করতে হয়। একটা ফুল জিসি সম্পন্ন হতে যদি ১ থেকে ৩ সেকেন্ডের বেশি সময় লাগে, তখন জিসি টিউন করা জরুরি।
পার্মানেন্ট জেনারেশনের কারণে যদি কখনো OutOfMemoryError হয়ে থাকে, তাহলে পার্মানেন্ট জেনারেশনের সাইজ বাড়ানো যায়। আমরা ইতিমধ্যে জেনেছি যে, -XX:PermSize ও -XX:MaxPermSize এ দুটো জেভিএম অপশন দিয়ে পার্মানেন্ট জেনারেশনের সাইজ নির্ধারণ করা যায়।
আমরা যদি দেখতে পাই যে অনেক বেশি সংখ্যক ফুল জিসি সম্পন্ন হচ্ছে, তাহলে ওল্ড জেনারেশনের সাইজ বাড়িয়ে দিতে পারি। তবে ওল্ড জেনারেশনের সাইজ বড় করলে ফুল জিসির সংখ্যা কমে গেলেও ফুল জিসি সম্পন্ন হবার সময়কাল বেড়ে যাবে। অপরপক্ষে, ওল্ড জেনারেশনের সাইজ কমালে ফুল জিসির সম্পন্ন হবার সময়কাল কমবে, কিন্তু ফুল জিসির সংখ্যা বাড়বে কিংবা OutOfMemoryError তখন বেশি ঘটতে পারে। আমরা ইতিমধ্যে আরও জেনেছি যে -XX:NewRatio জেভিএম অপশন দিয়ে ওল্ড জেনারেশন আর ইয়ং জেনারেশনের সাইজের অনুপাত নির্ধারণ করা যায়। যেমন, NewRatio এর ভ্যালূ ২ মানে হল ওল্ড জেনারেশন ঃ ইয়ং জেনারেশন হবে ২ঃ১। NewRatio এর ভ্যালূ গারবেজ কালেকসন পারফরমান্সে গুরুত্বপূর্ণ ভূমিকা রাখে। আমাদের অ্যাপ্লিকেশানের ধরন অনুযায়ী ওল্ড জেনারেশন ও নিউ জেনারেশন সাইজের অনুপাতের অপটিমাল ভ্যালূ খুঁজে বের করতে হবে।
রেফারেন্স
এই ব্লগটি লিখতে নিচের বই আর আর্টিকেলগুলোর সাহায্য নেয়া হয়েছেঃ