আমার সবেমাত্র একটি সাক্ষাত্কার ছিল, এবং আমাকে জাভা দিয়ে একটি মেমরি ফাঁস তৈরি করতে বলা হয়েছিল ।
বলা বাহুল্য, আমি কীভাবে কীভাবে এটি তৈরি করতে শুরু করব সে সম্পর্কে কোনও ধারণা ছাড়াই বেশ বোবা অনুভব করেছি।
একটি উদাহরণ কি হবে?
আমার সবেমাত্র একটি সাক্ষাত্কার ছিল, এবং আমাকে জাভা দিয়ে একটি মেমরি ফাঁস তৈরি করতে বলা হয়েছিল ।
বলা বাহুল্য, আমি কীভাবে কীভাবে এটি তৈরি করতে শুরু করব সে সম্পর্কে কোনও ধারণা ছাড়াই বেশ বোবা অনুভব করেছি।
একটি উদাহরণ কি হবে?
উত্তর:
খাঁটি জাভাতে সত্যিকারের মেমরি ফুটো (কোডগুলি চালিয়ে যাওয়া অ্যাক্সেসযোগ্য তবে এখনও মেমরিতে সংরক্ষণ করা আছে) তৈরি করার জন্য এখানে একটি ভাল উপায়:
ClassLoader
।new byte[1000000]
), এটির একটি দৃ reference় রেফারেন্স একটি স্ট্যাটিক ক্ষেত্রে সংরক্ষণ করে এবং তারপরে একটিতে নিজের একটি রেফারেন্স সঞ্চয় করে ThreadLocal
। অতিরিক্ত মেমরি বরাদ্দ করা alচ্ছিক (শ্রেণীর উদাহরণ ফাঁস যথেষ্ট) তবে এটি ফাঁসের কাজটি আরও দ্রুত করে তুলবে।ClassLoader
এটি লোড করা হয়েছিল।উপায়টি ThreadLocal
ওরাকলের জেডিকে বাস্তবায়নের কারণে, এটি একটি মেমরি ফাঁস তৈরি করে:
Thread
একটি ব্যক্তিগত ক্ষেত্র রয়েছে threadLocals
, যা প্রকৃতপক্ষে থ্রেড-স্থানীয় মান সংরক্ষণ করে।ThreadLocal
বস্তুর প্রতি দুর্বল রেফারেন্স , সুতরাং সেই ThreadLocal
বস্তুটি আবর্জনা-সংগ্রহের পরে মানচিত্র থেকে এর প্রবেশ সরিয়ে নেওয়া হয়।ThreadLocal
তার মূল বিষয়টিকে নির্দেশ করে , তখন সেই বস্তুটি না ময়লা আবর্জনা সংগ্রহ করা হবে না যতক্ষণ না থ্রেড বেঁচে থাকবে theএই উদাহরণে, শক্তিশালী রেফারেন্সগুলির শৃঙ্খলাটি এরকম দেখাচ্ছে:
Thread
অবজেক্ট → threadLocals
মানচিত্র example উদাহরণ শ্রেণীর উদাহরণ class উদাহরণ শ্রেণি → স্থির ThreadLocal
ক্ষেত্র → ThreadLocal
অবজেক্ট।
(এই ClassLoader
ফাঁস তৈরিতে সত্যই ভূমিকা রাখে না, এই অতিরিক্ত রেফারেন্স চেইনের কারণে এটি ফাঁসকে আরও খারাপ করে তোলে: উদাহরণস্বরূপ শ্রেণি → ClassLoader
→ এটি যে সমস্ত ক্লাসে লোড হয়েছে। অনেক জেভিএম বাস্তবায়নে এটি আরও খারাপ ছিল, বিশেষত পূর্বে জাভা,, কারণ ক্লাস এবং ClassLoader
এসগুলি সরাসরি পার্জেজে বরাদ্দ করা হয়েছিল এবং কখনই আবর্জনা সংগ্রহ করা হয়নি were)
এই প্যাটার্নটির একটি ভিন্নতা হ'ল অ্যাপ্লিকেশন পাত্রে (টমক্যাটের মতো) যদি আপনি ঘন ঘন অ্যাপ্লিকেশনগুলি পুনরায় বিতরণ করেন যা কোনওভাবে ThreadLocal
নিজের কাছে ফিরে আসে তবে অ্যাপ্লিকেশন পাত্রে একটি চালনীয়ের মতো মেমরি ফাঁস হয়। এটি বেশ কয়েকটি সূক্ষ্ম কারণে ঘটতে পারে এবং প্রায়শই ডিবাগ করা এবং / অথবা ঠিক করা কঠিন।
আপডেট : যেহেতু প্রচুর লোকেরা এটির জন্য জিজ্ঞাসা করে, তাই এখানে কয়েকটি উদাহরণ কোড রয়েছে যা এই আচরণটিকে কার্যত দেখায় ।
স্ট্যাটিক ফিল্ড হোল্ডিং অবজেক্ট রেফারেন্স [esp ফাইনাল ফিল্ড]
class MemorableClass {
static final ArrayList list = new ArrayList(100);
}
String.intern()
লম্বা স্ট্রিংয়ে ফোন করা
String str=readString(); // read lengthy string any source db,textbox/jsp etc..
// This will place the string in memory pool from which you can't remove
str.intern();
(অনাবদ্ধ) মুক্ত স্ট্রিম (ফাইল, নেটওয়ার্ক ইত্যাদি ...)
try {
BufferedReader br = new BufferedReader(new FileReader(inputFile));
...
...
} catch (Exception e) {
e.printStacktrace();
}
অনাবৃত সংযোগগুলি
try {
Connection conn = ConnectionFactory.getConnection();
...
...
} catch (Exception e) {
e.printStacktrace();
}
অঞ্চলগুলি যা জেভিএমের আবর্জনা সংগ্রাহকের কাছ থেকে অ্যাক্সেসযোগ্য নয় , যেমন দেশীয় পদ্ধতির মাধ্যমে বরাদ্দ করা মেমরি
ওয়েব অ্যাপ্লিকেশনগুলিতে, অ্যাপ্লিকেশন স্পষ্টভাবে বন্ধ বা অপসারণ না করা অবধি কিছু বস্তু অ্যাপ্লিকেশন স্কোপে সংরক্ষণ করা হয়।
getServletContext().setAttribute("SOME_MAP", map);
ভুল বা অনুপযুক্ত জেভিএম বিকল্পগুলি , যেমন noclassgc
আইবিএম জেডিকে বিকল্প যা অব্যবহৃত শ্রেণির আবর্জনা সংগ্রহকে বাধা দেয়
দেখুন আইবিএম JDK সেটিংস ।
close()
সাধারণত চূড়ান্তকরণে অন্তর্ভুক্ত থাকে না) থ্রেড যেহেতু একটি ব্লকিং অপারেশন হতে পারে)। এটি বন্ধ না করা একটি খারাপ অভ্যাস, তবে এটি ফুটো হওয়ার কারণ নয়। অনাবদ্ধ java.sql. সংযোগ একই।
intern
। যেমন, এটি সঠিকভাবে সংগ্রহ করা আবর্জনা নয় কোনও ফুটো। (তবে আইএএনএজেপি) মাইন্ডপ্রড.com
একটি সহজ কাজ হ'ল একটি ভুল (বা অস্তিত্বহীন) hashCode()
বা একটি হ্যাশসেট ব্যবহার করা equals()
এবং তারপরে "নকল" যোগ করা চালিয়ে যাওয়া। সদৃশগুলি যেমনটি হওয়া উচিত তা উপেক্ষা করার পরিবর্তে সেটটি কেবল কখনও বাড়বে এবং আপনি সেগুলি সরাতে পারবেন না।
আপনি যদি এই খারাপ কীগুলি / উপাদানগুলির চারদিকে ঝুলতে চান তবে আপনি একটি স্ট্যাটিক ফিল্ড ব্যবহার করতে পারেন
class BadKey {
// no hashCode or equals();
public final String key;
public BadKey(String key) { this.key = key; }
}
Map map = System.getProperties();
map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.
নীচে একটি অ-স্পষ্ট কেস থাকবে যেখানে ভুলে যাওয়া শ্রোতার স্ট্যান্ডার্ড কেস, স্থিতিক রেফারেন্স, হ্যাশম্যাপে বোগাস / পরিবর্তনযোগ্য কীগুলি বা তাদের জীবনচক্রটি শেষ করার কোনও সুযোগ ছাড়াই কেবল থ্রেড আটকে রয়েছে Java
File.deleteOnExit()
- সর্বদা স্ট্রিং ফাঁস, char[]
, সুতরাং পরে প্রয়োগ হয় না ; @ ড্যানিয়েল, যদিও ভোটের দরকার নেই।বেশিরভাগ অব্যবস্থাপনাযুক্ত থ্রেডের বিপদ দেখাতে আমি থ্রেডগুলিতে মনোনিবেশ করব, এমনকি দোলের ছোঁয়াতেও চাই না।
Runtime.addShutdownHook
এবং অপসারণ না ... এবং তারপরে এমনকি স্ট্রেডগ্রুপ শ্রেণিতে একটি ত্রুটিযুক্ত স্ট্র্যাডগ্রুপের কারণে স্ট্রেটগ্রুপটি সংগ্রহ না করা হতে পারে, থ্রেডগ্রুপটি কার্যকরভাবে ফাঁস হতে পারে। গসিপরোউটারে জেগ্রুপের ফুটো আছে।
তৈরি করা হচ্ছে, তবে শুরু হচ্ছে না, Thread
উপরের মত একই বিভাগে চলেছে।
একটি থ্রেড তৈরি করা উত্তরাধিকার সূত্রে প্রাপ্ত ContextClassLoader
এবং আরও AccessControlContext
, যে ThreadGroup
কোনও একটি InheritedThreadLocal
, সেই সমস্ত উল্লেখগুলি সম্ভাব্য ফাঁস সহ ক্লাস লোডার এবং সমস্ত স্ট্যাটিক রেফারেন্স এবং জা-জা-র দ্বারা লোড করা সমস্ত ক্লাসের সাথে থাকে। প্রভাবটি পুরো জুসিএক্সিকিউটর ফ্রেমওয়ার্কের সাথে বিশেষত দৃশ্যমান যা একটি সুপার সাধারণ ThreadFactory
ইন্টারফেসের বৈশিষ্ট্যযুক্ত , তবু বেশিরভাগ বিকাশকারীদের কাছে লুকোচুরি বিপদের কোনও চিহ্ন নেই। এছাড়াও প্রচুর লাইব্রেরি অনুরোধের ভিত্তিতে থ্রেড শুরু করে (অনেক বেশি জনপ্রিয় শিল্প জনপ্রিয় লাইব্রেরি)।
ThreadLocal
ক্যাশে; এগুলি অনেক ক্ষেত্রেই খারাপ। আমি নিশ্চিত যে থ্রেডলোকালের উপর ভিত্তি করে সকলেই বেশ কিছুটা সাধারণ ক্যাশে দেখেছেন, ভাল খবরটি: যদি থ্রেডটি জীবনের প্রত্যাশার তুলনায় ক্লাসলোডারের চেয়ে বেশি চলতে থাকে তবে এটি একটি খাঁটি সুন্দর সামান্য ফুটো। সত্যিই প্রয়োজন না হলে থ্রেডলোকাল ক্যাশে ব্যবহার করবেন না।
ThreadGroup.destroy()
থ্রেডগ্রুপের কোনও থ্রেড না থাকলে কল করা , তবে এটি চাইল্ড থ্রেডগ্রুপ রাখে। একটি খারাপ ফুটো যা থ্রেডগ্রুপকে তার পিতামাতার কাছ থেকে অপসারণ করতে বাধা দেবে, তবে সমস্ত বাচ্চা অগণিত হবে।
WeakHashMap এবং মানটি (ইন) সরাসরি কীটি উল্লেখ করে। হিপ ডাম্প ছাড়া এটি খুঁজে পাওয়া শক্ত। এটি সমস্ত বর্ধিত ক্ষেত্রে প্রযোজ্য Weak/SoftReference
যা রক্ষিত অবজেক্টটিতে কোনও শক্ত রেফারেন্স রাখতে পারে।
ব্যবহার java.net.URL
http (s) প্রোটোকল সঙ্গে থেকে সংস্থান লোড (!)। এটি একটি বিশেষ, এটি KeepAliveCache
থ্রেডগ্রুপ সিস্টেমে একটি নতুন থ্রেড তৈরি করে যা বর্তমান থ্রেডের প্রসঙ্গে ক্লাসলোডার ফাঁস করে। থ্রেডটি প্রথম অনুরোধের ভিত্তিতে তৈরি করা হয় যখন কোনও জীবন্ত থ্রেড উপস্থিত না থাকে, সুতরাং আপনি ভাগ্যবান হতে পারেন বা কেবল ফাঁস হন। ফাঁস ইতিমধ্যে জাভা 7 এ স্থির হয়েছে এবং থ্রেড সঠিকভাবে তৈরি করা কোডটি প্রসঙ্গ শ্রেণিবদ্ধকে সরিয়ে দেয়।আরও কয়েকটি মামলা রয়েছে (ইমেজফ্যাচারের মতো, এছাড়াও নির্দিষ্ট অনুরূপ থ্রেড তৈরি করার)।
ব্যবহার InflaterInputStream
ক্ষণস্থায়ী new java.util.zip.Inflater()
কন্সট্রাকটর মধ্যে ( PNGImageDecoder
উদাহরণস্বরূপ) এবং কলিং না end()
inflater করুন। ঠিক আছে, আপনি যদি কন্সট্রাক্টরে ন্যায্য new
, কোনও সুযোগ না দিয়ে পাস করেন ... এবং হ্যাঁ, close()
স্ট্রিমটিতে কল করা যদি ম্যানুয়ালি কন্সট্রাক্টর প্যারামিটার হিসাবে পাস হয় তবে এটি স্ফীতকটি বন্ধ করে না। এটি সত্যিকারের ফাঁস নয় কারণ এটি চূড়ান্তকরণকারী দ্বারা প্রকাশ করা হবে ... যখন এটি প্রয়োজনীয় মনে করবে ems এই মুহুর্ত পর্যন্ত এটি নেটিভ স্মৃতি এত খারাপভাবে খায় এটি লিনাক্স oom_killer কে দায়মুক্তি দিয়ে প্রক্রিয়াটি মেরে ফেলতে পারে। মূল সমস্যাটি হ'ল জাভাতে চূড়ান্তকরণ খুব বিশ্বাসযোগ্য নয় এবং জি 1 এটি 7.0.2 অবধি খারাপ করেছে। গল্পটির নৈতিকতা: যত তাড়াতাড়ি সম্ভব স্থানীয় উত্সগুলি ছেড়ে দিন; ফাইনালাইজারটি খুব গরিব।
একই ক্ষেত্রে java.util.zip.Deflater
। এটি ডিফ্লেটার জাভাতে ক্ষুধার্ত স্মৃতিশক্তি হওয়ায় এটি আরও খারাপ, অর্থাত্ সর্বদা 15 বিট (সর্বোচ্চ) এবং 8 মেমরি স্তর (9 ম্যাক্সিমাম) ব্যবহার করে কয়েকশ কেবি কে দেশীয় মেমরি বরাদ্দ করে। ভাগ্যক্রমে, Deflater
ব্যাপকভাবে ব্যবহৃত হয় না এবং আমার জ্ঞানের জেডিকে কোনও অপব্যবহার নেই। end()
আপনি নিজে নিজে একটি Deflater
বা তৈরি করলে সর্বদা কল করুন Inflater
। শেষ দুটির সেরা অংশ: আপনি উপলব্ধ সাধারণ প্রোফাইলিং সরঞ্জামগুলির মাধ্যমে সেগুলি খুঁজে পাবেন না।
(অনুরোধের পরে আমি যে আরও কিছু সময় নষ্ট করেছিলাম তা যুক্ত করতে পারি))
নিরাপদে থাকো এবং রইল শুভ কামনা; ফাঁস খারাপ!
Creating but not starting a Thread...
হ্যাঁ, কয়েক শতাব্দী আগে আমি এটিকে খারাপভাবে কামড়েছিলাম! (জাভা 1.3)
unstarted
গণনা কিন্তু যে প্রতিরোধ (ক্ষুদ্রতর মন্দ কিন্তু এখনও একটি লিক) অন্তক থেকে থ্রেড গ্রুপ
ThreadGroup.destroy()
থ্রেডগ্রুপের কোনও থ্রেড না থাকলে কল করা ..." অবিশ্বাস্যভাবে সূক্ষ্ম বাগ; আমি ঘন্টাখানেক ধরে এটি তাড়া করে এসেছি, পথভ্রষ্ট হয়েছি কারণ আমার নিয়ন্ত্রণে থ্রেডের গণনা করা জিইআইআই কিছুই দেখায় নি, তবে থ্রেড গ্রুপ এবং সম্ভবত কমপক্ষে একটি শিশু গ্রুপ চলে যাবে না।
এখানে বেশিরভাগ উদাহরণ "খুব জটিল"। তারা প্রান্ত মামলা। এই উদাহরণগুলির সাথে, প্রোগ্রামারটি একটি ভুল করেছে (যেমন সমান / হ্যাশকোডের নতুন সংজ্ঞা না দেয়), বা জেভিএম / জেভিএ (স্ট্যাটিক সহ শ্রেণীর বোঝা ...) দ্বারা কোণে কাটা হয়েছে। আমি মনে করি এটি কোনও সাক্ষাত্কার চান এমন উদাহরণ বা সবচেয়ে সাধারণ ক্ষেত্রে নয় not
তবে মেমরি ফাঁসের জন্য সত্যিই সহজ মামলা রয়েছে। আবর্জনা সংগ্রাহক কেবল সেই বিষয়টিকেই মুক্ত করেন যা আর উল্লেখ করা হয়নি। আমরা জাভা বিকাশকারী হিসাবে মেমরির যত্ন নেই। আমরা যখন প্রয়োজন তখন এটি বরাদ্দ করি এবং এটি স্বয়ংক্রিয়ভাবে মুক্তি দেওয়া হোক। ফাইন।
তবে যে কোনও দীর্ঘস্থায়ী অ্যাপ্লিকেশন ভাগ করে নেওয়ার ঝোঁক রয়েছে। এটি কিছু হতে পারে, স্ট্যাটিকস, সিঙ্গলেটন ... প্রায়শই অ-তুচ্ছ অ্যাপ্লিকেশনগুলি জটিল বিষয়গুলির গ্রাফ তৈরি করে to কেবল নুলের জন্য একটি রেফারেন্স সেট করতে ভুলে যাওয়া বা প্রায়শই একটি সংগ্রহ থেকে একটি অবজেক্ট সরানো ভুলে যাওয়া মেমরি ফাঁস করার জন্য যথেষ্ট।
অবশ্যই সব ধরণের শ্রোতা (ইউআই শ্রোতার মতো), ক্যাশে বা কোনও দীর্ঘস্থায়ী শেয়ার্ড স্টেট সঠিকভাবে পরিচালিত না হলে মেমরি ফাঁস তৈরি করতে ঝোঁক। যা বোঝা যাবে তা হ'ল এটি জাভা কর্নার কেস নয়, বা আবর্জনা সংগ্রহকারীর সমস্যা। এটি একটি ডিজাইনের সমস্যা। আমরা ডিজাইন করি যে আমরা একটি শ্রোতার দীর্ঘকালীন অবজেক্টে যুক্ত করি, তবে যখন আর প্রয়োজন হয় না তখন আমরা শ্রোতাদের সরিয়ে দেই না। আমরা বস্তুগুলি ক্যাশে করি, তবে সেগুলি ক্যাশে থেকে সরানোর কোনও কৌশল আমাদের কাছে নেই।
আমাদের কাছে একটি জটিল গ্রাফ থাকতে পারে যা পূর্ববর্তী অবস্থা সংরক্ষণ করে যা একটি গণনার দ্বারা প্রয়োজনীয়। তবে পূর্ববর্তী রাজ্য নিজেই এর আগে রাজ্যের সাথে যুক্ত ছিল এবং আরও অনেক কিছু।
যেমন আমাদের এসকিউএল সংযোগ বা ফাইলগুলি বন্ধ করতে হবে। আমাদের সংগ্রহ থেকে উপাদানগুলি বাতিল এবং সরানোর জন্য যথাযথ রেফারেন্স সেট করতে হবে। আমাদের সঠিক ক্যাচিং কৌশল থাকবে (সর্বাধিক মেমরির আকার, উপাদানগুলির সংখ্যা বা টাইমার)। সমস্ত শ্রেনী যা শ্রোতাকে অবহিত করার অনুমতি দেয় তাদের অবশ্যই একটি অ্যাডলিস্টনার এবং অপসারণলিস্টনার পদ্ধতি উভয়ই সরবরাহ করতে হবে। এবং যখন এই নোটিফায়ারগুলি আর ব্যবহার না করা হয়, তাদের অবশ্যই তাদের শ্রোতার তালিকা সাফ করতে হবে।
একটি মেমরি ফাঁস সত্যই সম্ভব এবং পুরোপুরি অনুমানযোগ্য। বিশেষ ভাষার বৈশিষ্ট্য বা কোণার কেসের প্রয়োজন নেই। মেমরি ফাঁস হয় এমন একটি সূচক যে কোনও কিছু অনুপস্থিত বা এমনকি ডিজাইনের সমস্যা রয়েছে।
WeakReference
) না থাকলেও এক থেকে অন্য। যদি কোনও অবজেক্টের রেফারেন্সের অতিরিক্ত বিট থাকে, তবে এটি "টার্গেটের প্রতি যত্নশীল" সূচক রাখতে সহায়ক হতে পারে ...
PhantomReference
যদি কোনও বস্তু এটির যত্নবান কাউকে না পাওয়া যায় তবে সিস্টেমে বিজ্ঞপ্তিগুলি (অনুরূপ মাধ্যমের মাধ্যমে ) সরবরাহ করতে হবে। WeakReference
কিছুটা কাছাকাছি আসে, তবে এটি ব্যবহারের আগে অবশ্যই দৃ strong় রেফারেন্সে রূপান্তর করতে হবে; শক্তিশালী রেফারেন্স বিদ্যমান থাকাকালীন যদি কোনও জিসি চক্র ঘটে তবে লক্ষ্যটিকে কার্যকর বলে ধরে নেওয়া হবে।
উত্তরটি পুরোপুরি নির্ভর করে যে সাক্ষাত্কারকারীর তারা কী জিজ্ঞাসা করেছিল।
অনুশীলনে জাভা ফুটা করা কি সম্ভব? অবশ্যই এটি, এবং অন্যান্য উত্তরগুলিতে প্রচুর উদাহরণ রয়েছে।
তবে একাধিক মেটা-প্রশ্ন রয়েছে যা জিজ্ঞাসা করা হতে পারে?
আমি আপনার মেটা-প্রশ্নটি "এই সাক্ষাত্কারের পরিস্থিতিতে আমি কী উত্তর দিতে পারি" হিসাবে পড়ছি। এবং তাই, আমি জাভা পরিবর্তে সাক্ষাত্কার দক্ষতার উপর ফোকাস করতে যাচ্ছি। আমি বিশ্বাস করি যে জাভা ফাঁস কীভাবে করা যায় তা জানার প্রয়োজন হওয়ার চেয়ে আপনি কোনও সাক্ষাত্কারে কোনও প্রশ্নের উত্তর না জেনে পরিস্থিতি পুনরুক্ত করার সম্ভাবনা বেশি। সুতরাং, আশা করি, এটি সাহায্য করবে।
সাক্ষাত্কারের জন্য আপনি যে সর্বাধিক গুরুত্বপূর্ণ বিকাশ করতে পারেন তার মধ্যে একটি হ'ল সক্রিয়ভাবে প্রশ্নগুলি শুনতে শেখা এবং সাক্ষাত্কারকারীর সাথে তাদের অভিপ্রায় আহরণ করার জন্য কাজ করা। এটি আপনাকে যেভাবে চায় সেভাবেই তাদের প্রশ্নের উত্তর দেয় না, তবে এটি দেখায় যে আপনার কাছে কিছু গুরুত্বপূর্ণ যোগাযোগ দক্ষতা রয়েছে। এবং যখন এটি অনেক সমান দক্ষ প্রতিভাশালী বিকাশকারীদের মধ্যে একটি পছন্দ নেমে আসে, আমি প্রতিবার প্রতিক্রিয়া জানানোর আগে যিনি শোনেন, ভাবেন এবং বোঝেন তাকে আমি নিয়োগ করব।
আপনি জেডিবিসি না বুঝতে পারলে নীচে একটি দুর্দান্ত অর্থহীন উদাহরণ । বা অন্তত কিভাবে JDBC এর নিকট একজন বিকাশকারী আশা এ Connection
, Statement
এবং ResultSet
তাদের খারিজ অথবা তাদের উল্লেখ হারানোর পরিবর্তে বাস্তবায়ন জানানোর আগে দৃষ্টান্ত finalize
।
void doWork()
{
try
{
Connection conn = ConnectionFactory.getConnection();
PreparedStatement stmt = conn.preparedStatement("some query"); // executes a valid query
ResultSet rs = stmt.executeQuery();
while(rs.hasNext())
{
... process the result set
}
}
catch(SQLException sqlEx)
{
log(sqlEx);
}
}
উপরের সমস্যাটি হ'ল এই Connection
অবজেক্টটি বন্ধ নয়, এবং তাই আবর্জনা সংগ্রহকারী আশেপাশে না এসে এটি অ্যাক্সেসযোগ্য না হওয়া অবধি শারীরিক সংযোগটি উন্মুক্ত থাকবে। জিসি finalize
পদ্ধতিটি গ্রহণ করবে, তবে জেডিবিসি ড্রাইভার রয়েছে যা বাস্তবায়িত হয় না finalize
, অন্তত Connection.close
বাস্তবায়িত একইভাবে নয় । ফলস্বরূপ আচরণটি হ'ল অ্যাক্সেসযোগ্য অবজেক্টগুলি সংগ্রহ করার কারণে স্মৃতি পুনরুদ্ধার করা হবে, সেই সাথে সংযুক্ত সংস্থানসমূহ (মেমরি সহ)Connection
অবজেক্টের কেবল পুনরুদ্ধারযোগ্য নয়।
যেমন একটি ঘটনা যেখানে সালে Connection
'র finalize
পদ্ধতি না পরিষ্কার আপ সবকিছু করে, এক আসলে পেতে পারে যতক্ষণ না ডাটাবেজ সার্ভারের অবশেষে সংখ্যাগুলিকে যে সংযোগ জীবিত নয় যে ডাটাবেজ সার্ভারের শারীরিক সংযোগ, স্থায়ী হবে বিভিন্ন গার্বেজ কালেকশন চক্র (যদি এটি করে), এবং বন্ধ করা উচিত।
এমনকি জেডিবিসি ড্রাইভার প্রয়োগ করতে গেলেও finalize
চূড়ান্তকরণের সময় ব্যতিক্রম ছুঁড়ে ফেলা সম্ভব। ফলস্বরূপ আচরণটি হ'ল এখন "সুপ্ত" অবজেক্টের সাথে যুক্ত যে কোনও স্মৃতি পুনরুদ্ধার করা হবে না, যেমনটি finalize
একবার মাত্র আহ্বান করার গ্যারান্টিযুক্ত।
অবজেক্ট চূড়ান্তকরণের সময় ব্যতিক্রমগুলির মুখোমুখি হওয়ার উপরের দৃশ্যটি অন্য একটি দৃশ্যের সাথে সম্পর্কিত যা সম্ভবত একটি স্মৃতি ফাঁস হতে পারে - অবজেক্টের পুনরুত্থান। অবজেক্টের পুনরুত্থান প্রায়শই অন্য বস্তু থেকে চূড়ান্ত হওয়া থেকে অবজেক্টের একটি শক্তিশালী রেফারেন্স তৈরি করে উদ্দেশ্যমূলকভাবে করা হয়। যখন অবজেক্টের পুনরুত্থানের অপব্যবহার করা হয় তখন এটি মেমরি ফাঁসের অন্যান্য উত্সগুলির সাথে মেমরির ফাঁস হতে পারে to
এমন আরও অনেকগুলি উদাহরণ রয়েছে যা আপনি জাঁকতে পারেন - পছন্দ করুন
List
আপনি কেবল তালিকায় যুক্ত হচ্ছেন এবং এটি থেকে মুছে ফেলা হচ্ছে না এমন একটি উদাহরণ পরিচালনা করা (যদিও আপনার প্রয়োজন মতো উপাদানগুলি থেকে মুক্তি পাওয়া উচিত), বাSocket
এস বা File
গুলি খুলছে , তবে যখন তাদের আর প্রয়োজন হবে না তখন তাদের বন্ধ করে দেওয়া হবে না ( Connection
শ্রেণীর সাথে জড়িত উপরের উদাহরণের মতো)।Connection.close
সমস্ত এসকিউএল কলগুলির অবশেষে অবধি প্রবেশ না করা অবধি আমি এসকিউএল ডাটাবেসের সংযোগ সীমাটিকে অনেক হিট করেছি। অতিরিক্ত মজাদার জন্য আমি কিছু দীর্ঘ চলমান ওরাকল সঞ্চিত প্রক্রিয়া কল করেছিলাম যা জাভা সাইডে লক প্রয়োজন যা ডাটাবেসে খুব বেশি কল রোধ করতে পারে।
সম্ভাব্য মেমরি ফুটো এবং সম্ভবত কীভাবে এড়ানো যায় তার অন্যতম সহজ উদাহরণ হ'ল অ্যারেলিস্ট.রেমোভ (ইনট) এর বাস্তবায়ন:
public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
elementData[--size] = null; // (!) Let gc do its work
return oldValue;
}
আপনি যদি এটি নিজে প্রয়োগ করে থাকেন তবে আপনি কী অ্যারে এলিমেন্টটি আর ব্যবহার করবেন না তা পরিষ্কার করার চিন্তা করতেন ( elementData[--size] = null
)? এই রেফারেন্সটি একটি বিশাল অবজেক্টকে বাঁচিয়ে রাখতে পারে ...
যে কোনও সময় আপনি অবজেক্টগুলির আশেপাশে রেফারেন্স রাখেন যে আপনার আর মেমরি ফুটো হওয়ার দরকার নেই। জাভা প্রোগ্রামগুলিতে মেমরি ফাঁস কীভাবে জাভায় প্রকাশ পায় এবং আপনি এটি সম্পর্কে কী করতে পারেন তার উদাহরণগুলির জন্য জাভা প্রোগ্রামগুলিতে মেমরি ফাঁস পরিচালনা করা দেখুন ।
...then the question of "how do you create a memory leak in X?" becomes meaningless, since it's possible in any language.
আপনি কীভাবে এই উপসংহারটি আঁকছেন তা আমি দেখতে পাচ্ছি না। যে কোনও সংজ্ঞায় জাভাতে মেমরি ফুটো তৈরির কয়েকটি উপায় রয়েছে। এটি অবশ্যই একটি বৈধ প্রশ্ন।
আপনি sun.misc.Unsafe শ্রেণীর সাহায্যে মেমরি ফাঁস করতে সক্ষম । আসলে এই পরিষেবা শ্রেণিটি বিভিন্ন স্ট্যান্ডার্ড ক্লাসে ব্যবহৃত হয় (উদাহরণস্বরূপ জাভা.নিও ক্লাসে)। আপনি সরাসরি এই শ্রেণীর উদাহরণ তৈরি করতে পারবেন না , তবে আপনি এটি করতে প্রতিচ্ছবি ব্যবহার করতে পারেন ।
Eclipse IDE- এ কোডটি সংকলন করে না - কমান্ড ব্যবহার করে এটি javac
সংকলন করুন (সংকলনের সময় আপনি সতর্কতা পাবেন)
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class TestUnsafe {
public static void main(String[] args) throws Exception{
Class unsafeClass = Class.forName("sun.misc.Unsafe");
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
System.out.print("4..3..2..1...");
try
{
for(;;)
unsafe.allocateMemory(1024*1024);
} catch(Error e) {
System.out.println("Boom :)");
e.printStackTrace();
}
}
}
আমি আমার উত্তরটি এখানে থেকে অনুলিপি করতে পারি: জাভাতে মেমরি ফাঁস হওয়ার সবচেয়ে সহজ উপায়?
"কম্পিউটার বিজ্ঞানে (বা এই প্রসঙ্গে ফাঁস) একটি মেমরি ফাঁস হয় যখন কোনও কম্পিউটার প্রোগ্রাম মেমরি গ্রহণ করে তবে অপারেটিং সিস্টেমে এটি পুনরায় মুক্তি দিতে অক্ষম হয়" " (উইকিপিডিয়া)
সহজ উত্তর: আপনি পারবেন না। জাভা স্বয়ংক্রিয় মেমরি পরিচালনা করে এবং আপনার প্রয়োজনীয় নয় এমন সংস্থানগুলি মুক্ত করবে। আপনি এটি হতে আটকাতে পারবেন না। এটি সর্বদা সংস্থানগুলি মুক্তি দিতে সক্ষম হবে। ম্যানুয়াল মেমরি পরিচালনা সহ প্রোগ্রামগুলিতে, এটি আলাদা। আপনি সি তে ম্যালোক () ব্যবহার করে কিছু স্মৃতি পেতে পারেন না। মেমরিটি মুক্ত করতে, আপনাকে পয়েন্টারটির দরকার যা মলোক ফিরে এসেছে এবং এটিতে ফ্রি () কল করুন। তবে যদি আপনার আর পয়েন্টার না থাকে (ওভাররাইট, বা আজীবন ছাড়িয়ে গেছে), তবে দুর্ভাগ্যক্রমে আপনি এই মেমরিটি মুক্ত করতে অক্ষম এবং এইভাবে আপনার মেমরি ফাঁস হয়।
এখনও অবধি অন্যান্য সমস্ত উত্তর আমার সংজ্ঞায় আসলে মেমরি ফাঁস নয়। এগুলি সবার লক্ষ্য নিখরচায় বাস্তবের সাথে দ্রুত মেমরিটি পূরণ করা। তবে যে কোনও সময়ে আপনি তৈরি করেছেন এমন অবজেক্টগুলিকে এখনও অবাস্তব করতে পারেন এবং এইভাবে মেমরিটি মুক্ত করে -> কোনও লিক করুন। অ্যাকনরাডের উত্তরটি খুব কাছাকাছি আসে যদিও আমাকে স্বীকার করতে হয় যেহেতু তার সমাধান কার্যকরভাবে আবর্জনা সংগ্রহকারীকে এটি একটি অন্তহীন লুপে জোর করে "ক্র্যাশ" করা কার্যকরভাবে কার্যকর)।
দীর্ঘ উত্তরটি: আপনি জেএনআই ব্যবহার করে জাভাটির জন্য একটি লাইব্রেরি লিখে মেমরি ফাঁস পেতে পারেন, যার ম্যানুয়াল মেমরি পরিচালনা থাকতে পারে এবং মেমরি ফাঁস থাকতে পারে। আপনি যদি এই লাইব্রেরিটি কল করেন তবে আপনার জাভা প্রক্রিয়াটি মেমরি ফাঁস করবে। অথবা, আপনার জেভিএম-তে বাগ থাকতে পারে, যাতে জেভিএম স্মৃতি হারিয়ে ফেলে। সম্ভবত জেভিএম-তে বাগ রয়েছে, এমনকী কিছু জ্ঞাত থাকতে পারে যেহেতু আবর্জনা সংগ্রহ করা তুচ্ছ নয়, তবে এটি এখনও একটি বাগ। ডিজাইন দ্বারা এটি সম্ভব নয়। আপনি এমন কিছু জাভা কোড চাইছেন যা এইরকম বাগ দ্বারা প্রভাবিত। দুঃখিত, আমি একটি জানি না এবং এটি পরবর্তী জাভা সংস্করণে যাইহোক, সম্ভবত এটি কোনও বাগ নাও হতে পারে।
Http://wiki.eclipse.org/Performance_Bloopers#String.substring.28.29- এর মাধ্যমে এখানে একটি সরল / অশুভ ।
public class StringLeaker
{
private final String muchSmallerString;
public StringLeaker()
{
// Imagine the whole Declaration of Independence here
String veryLongString = "We hold these truths to be self-evident...";
// The substring here maintains a reference to the internal char[]
// representation of the original string.
this.muchSmallerString = veryLongString.substring(0, 1);
}
}
যেহেতু সাবস্ট্রিং মূলটির অভ্যন্তরীণ প্রতিনিধিত্বকে বোঝায়, দীর্ঘতর স্ট্রিং, আসল স্মৃতিতে থাকে। সুতরাং, যতক্ষণ না আপনি স্ট্রিংলিকার প্লে করছেন, আপনার স্মৃতিতেও পুরো আসল স্ট্রিং রয়েছে, এমনকি আপনি যদি মনে করেন যে আপনি কেবল একটি একক চরিত্রের স্ট্রিং ধরে আছেন।
মূল স্ট্রিংয়ের সাথে অযাচিত রেফারেন্সটি সংরক্ষণ না করার উপায় হ'ল এইরকম কিছু করা:
...
this.muchSmallerString = new String(veryLongString.substring(0, 1));
...
খারাপ যুক্ত করার জন্য, আপনি .intern()
সাবস্ট্রিংও করতে পারেন :
...
this.muchSmallerString = veryLongString.substring(0, 1).intern();
...
এটি করার ফলে স্ট্রিংলিকার উদাহরণটি বাতিল করার পরেও মূল লং স্ট্রিং এবং উত্পন্ন সাবস্ট্রিং উভয়ই মেমোরিতে থাকবে।
muchSmallerString
হয় (কারণ StringLeaker
বস্তুটি ধ্বংস হয়ে যায়), দীর্ঘ স্ট্রিংটিও মুক্ত করা হবে। আমি যাকে মেমরি ফাঁস বলি তা হ'ল মেমরি যা জেভিএম-এর এই দৃষ্টিতে কখনই মুক্ত করা যায় না। যাইহোক, যদি আপনি নিজেকে দেখানো হয়েছে কিভাবে মেমরির মুক্ত করতে: this.muchSmallerString=new String(this.muchSmallerString)
। একটি সত্যিকারের মেমরি ফুটো হওয়ার সাথে, আপনার করার মতো কিছুই নেই।
intern
কেসটি "মেমরি ফাঁস" এর চেয়ে "মেমোরি অবাক" হতে পারে। .intern()
স্ট্রিংগুলিকে আইএনਗ দিলে অবশ্যই এমন একটি পরিস্থিতি তৈরি হয় যেখানে দীর্ঘ স্ট্রিংয়ের রেফারেন্সটি সংরক্ষিত থাকে এবং মুক্ত করা যায় না।
জিইউআই কোডে এর একটি সাধারণ উদাহরণ হ'ল যখন কোনও উইজেট / উপাদান তৈরি করা হয় এবং শ্রোতাকে কিছু স্ট্যাটিক / অ্যাপ্লিকেশন স্কোপড অবজেক্টে যুক্ত করা হয় এবং তারপরে উইজেটটি ধ্বংস হয়ে যায় তখন শ্রোতাকে সরিয়ে না দেওয়া হয়। আপনি কেবল একটি মেমরি ফুটোই পান না, পারফরম্যান্সেও হিট হন যখন আপনি আগুনের ঘটনাগুলি যা শুনছেন তা আপনার সমস্ত পুরানো শ্রোতাদেরও বলা হয়।
যে কোনও সার্লেট পাত্রে চলমান কোনও ওয়েব অ্যাপ্লিকেশন নিন (টমক্যাট, জেটি, গ্লাসফিশ, যাই হোক না কেন ...)। অ্যাপ্লিকেশনটি পর পর 10 বা 20 বার পুনরায় চালিত করুন (এটি সার্ভারের অটোডেপ্লাই ডিরেক্টরিতে কেবল ওয়ার স্পর্শ করার পক্ষে যথেষ্ট।
প্রকৃতপক্ষে যদি কেউ এটি পরীক্ষা না করে থাকে তবে সম্ভাবনা বেশি থাকে যে আপনি বেশ কয়েকবার পুনর্নির্মাণের পরে একটি আউটআফমিউরিওর পেয়ে যাবেন, কারণ অ্যাপ্লিকেশনটি নিজের পরে পরিষ্কার করার যত্ন নেয়নি। এমনকি আপনি এই পরীক্ষার মাধ্যমে আপনার সার্ভারে একটি বাগ খুঁজে পেতে পারেন।
সমস্যাটি হল, ধারকটির আজীবন আপনার আবেদনের আজীবনের চেয়ে দীর্ঘ। আপনাকে অবশ্যই নিশ্চিত করতে হবে যে আপনার অ্যাপ্লিকেশনটির ধারকগুলিতে বা ক্লাসগুলির মধ্যে থাকা সমস্ত উল্লেখগুলি আবর্জনা সংগ্রহ করতে পারে।
যদি আপনার ওয়েব অ্যাপ্লিকেশনটির অপ্রয়োজনীয় কর্মসংস্থানের মধ্যে কেবলমাত্র একটি রেফারেন্স থাকে তবে সংশ্লিষ্ট শ্রেণি লোডার এবং ফলস্বরূপ আপনার ওয়েব অ্যাপ্লিকেশনের সমস্ত শ্রেণি আবর্জনা সংগ্রহ করা যাবে না।
আপনার অ্যাপ্লিকেশন দ্বারা সূচিত থ্রেডস, থ্রেডলোকাল ভেরিয়েবল, লগিং অ্যাপেন্ডারগণ শ্রেণি লোডার ফাঁস হওয়ার জন্য কিছু সাধারণ সন্দেহভাজন।
জেএনআই এর মাধ্যমে বাহ্যিক নেটিভ কোড ব্যবহার করে?
খাঁটি জাভা দিয়ে এটি প্রায় অসম্ভব।
তবে এটি একটি "স্ট্যান্ডার্ড" ধরণের মেমরি ফাঁস, যখন আপনি আর মেমরি অ্যাক্সেস করতে পারবেন না তবে এটি এখনও অ্যাপ্লিকেশনটির মালিকানাধীন। পরিবর্তে অব্যবহৃত অবজেক্টের জন্য রেফারেন্স রাখতে পারেন, বা স্ট্রিমগুলি পরে বন্ধ না করেই খুলতে পারেন।
পারমজেন এবং এক্সএমএল পার্সিংয়ের সাথে আমার একবার "মেমরি ফুটো" হয়েছে। তুলনাটি দ্রুততর করার জন্য, আমরা যে এক্সএমএল পার্সারটি ব্যবহার করেছি (তা স্মরণ করতে পারি না যে এটি কোনটি ছিল) ট্যাগ নামগুলিতে একটি স্ট্রিং.ইনটার্ন () করেছে। আমাদের গ্রাহকের একের কাছে এক্সএমএল বৈশিষ্ট্য বা পাঠ্যে না হয়ে ডেটা মান সংরক্ষণ করার দুর্দান্ত ধারণা ছিল, তবে আমাদের কাছে একটি নথি ছিল:
<data>
<1>bla</1>
<2>foo</>
...
</data>
প্রকৃতপক্ষে, তারা সংখ্যাগুলি ব্যবহার করেনি তবে দীর্ঘতর পাঠ্য আইডি (প্রায় 20 টি অক্ষর), যা অনন্য ছিল এবং দিনে 10-15 মিলিয়ন হারে আসে। এটি দিনে 200 এমবি জঞ্জাল করে তোলে, যা আর কখনও প্রয়োজন হয় না এবং কখনও জিসিড হয় না (যেহেতু এটি পারমজেনে রয়েছে)। আমাদের 512 এমবি সেট করা হয়েছে, সুতরাং স্মৃতি বহির্ভূত ব্যতিক্রম (OOM) আসতে প্রায় দুই দিন সময় লাগল ...
একটি স্মৃতি ফাঁস কি:
সাধারণ উদাহরণ:
বিষয়বস্তুগুলির একটি ক্যাশে জিনিস জগাখিচুড়ি করার একটি ভাল সূচনা পয়েন্ট।
private static final Map<String, Info> myCache = new HashMap<>();
public void getInfo(String key)
{
// uses cache
Info info = myCache.get(key);
if (info != null) return info;
// if it's not in cache, then fetch it from the database
info = Database.fetch(key);
if (info == null) return null;
// and store it in the cache
myCache.put(key, info);
return info;
}
আপনার ক্যাশে বৃদ্ধি এবং বড় হয়। এবং খুব শীঘ্রই পুরো ডাটাবেস মেমরি চুষতে হবে। একটি ভাল ডিজাইন একটি LRUMap ব্যবহার করে (কেবল সম্প্রতি ব্যবহৃত জিনিসগুলিকে ক্যাশে রাখে)।
অবশ্যই, আপনি জিনিসগুলি আরও জটিল করে তুলতে পারেন:
যা প্রায়শই ঘটে:
যদি এই তথ্য অবজেক্টে অন্যান্য অবজেক্টের রেফারেন্স থাকে তবে এর সাথে আবার অন্য অবজেক্টের রেফারেন্স রয়েছে। একটি উপায়ে আপনি এটিকে একরকমের মেমরি ফাঁস, (খারাপ ডিজাইনের কারণে) বিবেচনা করতে পারেন।
আমি মনে করি এটি আকর্ষণীয় যে কেউ অভ্যন্তরীণ শ্রেণীর উদাহরণ ব্যবহার করেনি। আপনার যদি অভ্যন্তরীণ শ্রেণি থাকে; এটি সহজাতভাবে সমন্বিত শ্রেণীর একটি রেফারেন্স বজায় রাখে। অবশ্যই এটি প্রযুক্তিগতভাবে কোনও মেমরি ফুটো নয় কারণ জাভা শেষ পর্যন্ত এটি পরিষ্কার করবে; তবে এর ফলে ক্লাসগুলি প্রত্যাশার চেয়ে দীর্ঘ সময়ের মধ্যে ঝুলতে পারে।
public class Example1 {
public Example2 getNewExample2() {
return this.new Example2();
}
public class Example2 {
public Example2() {}
}
}
এখন আপনি যদি উদাহরণ 1 এ কল করুন এবং একটি উদাহরণ 2 ছাড়ুন উদাহরণ 1, আপনার কাছে সহজাতভাবে এখনও উদাহরণ 1 অবজেক্টের লিঙ্ক থাকবে।
public class Referencer {
public static Example2 GetAnExample2() {
Example1 ex = new Example1();
return ex.getNewExample2();
}
public static void main(String[] args) {
Example2 ex = Referencer.GetAnExample2();
// As long as ex is reachable; Example1 will always remain in memory.
}
}
আমি একটি গুজবও শুনেছি যে আপনার যদি একটি পরিবর্তনশীল থাকে যা নির্দিষ্ট সময়ের চেয়ে বেশি সময়ের জন্য বিদ্যমান থাকে; জাভা অনুমান করে যে এটি সর্বদা বিদ্যমান থাকবে এবং আসলে কোডে পৌঁছানো না গেলে এটি পরিষ্কার করার চেষ্টা করবে না। তবে তা সম্পূর্ণ যাচাই করা হয়নি।
লগ 4 জে করে সম্প্রতি আমি একটি মেমরি ফাঁস হওয়ার পরিস্থিতির মুখোমুখি হয়েছি।
লগ 4 জে নেস্টেড ডায়াগনস্টিক কনটেক্সট (এনডিসি) নামে পরিচিত এই প্রক্রিয়াটি রয়েছে যা বিভিন্ন উত্স থেকে আন্তঃবাহিত লগ আউটপুটকে আলাদা করার একটি উপকরণ। এনডিসি যে গ্রানুলারিটিতে কাজ করে তা হ'ল থ্রেড, তাই এটি লগ আউটপুটগুলি পৃথকভাবে বিভিন্ন থ্রেড থেকে পৃথক করে।
থ্রেড নির্দিষ্ট ট্যাগগুলি সংরক্ষণ করার জন্য, লগ 4 জের এনডিসি ক্লাস একটি হ্যাশটেবল ব্যবহার করে যা থ্রেড অবজেক্ট নিজেই থ্রেড করে থাকে (থ্রেড আইডি বলার বিপরীতে), এবং এভাবে এনডিসি ট্যাগ থ্রেডের বাইরে থাকা সমস্ত বস্তু স্মৃতিতে থাকে না till বস্তু স্মৃতিতে থাকা। আমাদের ওয়েব অ্যাপ্লিকেশনটিতে আমরা একটি অনুরোধ আইডি দিয়ে লগআউটপুটগুলিকে ট্যাগ করতে একটি একক অনুরোধ থেকে আলাদাভাবে লগকে আলাদা করতে এনডিসি ব্যবহার করি। কোনও ধারক যা এনডিসি ট্যাগকে থ্রেডের সাথে যুক্ত করে, কোনও অনুরোধ থেকে প্রতিক্রিয়া ফিরিয়ে দেওয়ার সময় এটি সরিয়ে দেয়। একটি অনুরোধ প্রক্রিয়াকরণের সময় যখন সমস্যাটি ঘটেছিল তখন একটি শিশু থ্রেড তৈরি হয়েছিল যা নিম্নলিখিত কোডের মতো:
pubclic class RequestProcessor {
private static final Logger logger = Logger.getLogger(RequestProcessor.class);
public void doSomething() {
....
final List<String> hugeList = new ArrayList<String>(10000);
new Thread() {
public void run() {
logger.info("Child thread spawned")
for(String s:hugeList) {
....
}
}
}.start();
}
}
সুতরাং এনডিসির একটি প্রসঙ্গটি ইনলাইন থ্রেডের সাথে জড়িত ছিল যা তৈরি হয়েছিল। থ্রেড অবজেক্ট যা এই এনডিসি প্রসঙ্গে মূল বিষয় ছিল তা হল ইনলাইন থ্রেড যা এতে বিশাল তালিকাভুক্ত বস্তুটি বন্ধ হয়ে আছে। সুতরাং থ্রেডটি যা করছে তা শেষ করার পরেও, বিশাল তালিকাভুক্তের রেফারেন্সটি এনডিসি প্রসঙ্গে হেস্টেবল দ্বারা বেঁচে রেখেছিল, ফলে স্মৃতি ফাঁস হয়ে যায়।
সাক্ষাত্কারকারী সম্ভবত নীচের কোডটির মতো একটি বিজ্ঞপ্তি রেফারেন্সের সন্ধান করছিলেন (যা ঘটনাক্রমে কেবলমাত্র খুব পুরানো জেভিএমগুলিতে রেফারেন্স গণনা ব্যবহার করে যা মেমরি ফাঁস করে, যা আর ঘটনা নয়)। তবে এটি একটি খুব অস্পষ্ট প্রশ্ন, সুতরাং আপনার জেভিএম মেমরি পরিচালনা সম্পর্কে আপনার উপলব্ধি প্রদর্শন করার একটি প্রধান সুযোগ।
class A {
B bRef;
}
class B {
A aRef;
}
public class Main {
public static void main(String args[]) {
A myA = new A();
B myB = new B();
myA.bRef = myB;
myB.aRef = myA;
myA=null;
myB=null;
/* at this point, there is no access to the myA and myB objects, */
/* even though both objects still have active references. */
} /* main */
}
তারপরে আপনি ব্যাখ্যা করতে পারেন যে রেফারেন্স গণনা সহ উপরের কোডটি মেমরি ফাঁস করবে। তবে বেশিরভাগ আধুনিক জেভিএমরা আর গণনা গণনা আর ব্যবহার করে না, বেশিরভাগ সুইপ আবর্জনা সংগ্রহকারী ব্যবহার করে, যা প্রকৃতপক্ষে এই স্মৃতিটি সংগ্রহ করবে।
এরপরে আপনি কোনও অবজেক্ট তৈরির ব্যাখ্যা করতে পারেন যার অন্তর্নিহিত দেশীয় উত্স রয়েছে, এর মতো:
public class Main {
public static void main(String args[]) {
Socket s = new Socket(InetAddress.getByName("google.com"),80);
s=null;
/* at this point, because you didn't close the socket properly, */
/* you have a leak of a native descriptor, which uses memory. */
}
}
তারপরে আপনি ব্যাখ্যা করতে পারেন এটি প্রযুক্তিগতভাবে একটি মেমরি ফাঁস, তবে সত্যিই লিকটি জাভিএমের মূল স্থানীয় সংস্থানগুলি বরাদ্দকারী নেটিভ কোডের কারণে ঘটেছিল, যা আপনার জাভা কোড দ্বারা মুক্ত হয়নি।
দিনের শেষে, একটি আধুনিক জেভিএম সহ, আপনাকে কিছু জাভা কোড লিখতে হবে যা জেভিএমের সচেতনতার স্বাভাবিক ক্ষেত্রের বাইরে একটি নেটিভ রিসোর্স বরাদ্দ করে।
একটি স্থিতিশীল মানচিত্র তৈরি করুন এবং এতে কঠোর উল্লেখ যুক্ত করুন। এগুলি কখনই জিসিডি হবে না।
public class Leaker {
private static final Map<String, Object> CACHE = new HashMap<String, Object>();
// Keep adding until failure.
public static void addToCache(String key, Object value) { Leaker.CACHE.put(key, value); }
}
শ্রেণীর চূড়ান্তকরণ পদ্ধতিতে কোনও শ্রেণীর একটি নতুন উদাহরণ তৈরি করে আপনি চলন্ত মেমরি ফাঁস তৈরি করতে পারেন। চূড়ান্তকরণকারীর একাধিক উদাহরণ তৈরি হলে বোনাস পয়েন্ট। এখানে একটি সাধারণ প্রোগ্রাম যা আপনার গাদা আকারের উপর নির্ভর করে কয়েক সেকেন্ড এবং কয়েক মিনিটের মধ্যে পুরো স্তূপটি ফাঁস করে দেয়:
class Leakee {
public void check() {
if (depth > 2) {
Leaker.done();
}
}
private int depth;
public Leakee(int d) {
depth = d;
}
protected void finalize() {
new Leakee(depth + 1).check();
new Leakee(depth + 1).check();
}
}
public class Leaker {
private static boolean makeMore = true;
public static void done() {
makeMore = false;
}
public static void main(String[] args) throws InterruptedException {
// make a bunch of them until the garbage collector gets active
while (makeMore) {
new Leakee(0).check();
}
// sit back and watch the finalizers chew through memory
while (true) {
Thread.sleep(1000);
System.out.println("memory=" +
Runtime.getRuntime().freeMemory() + " / " +
Runtime.getRuntime().totalMemory());
}
}
}
আমার মনে হয় না যে কেউ এটি এখনও বলেছেন: আপনি চূড়ান্তকরণ () পদ্ধতিটিকে চূড়ান্ত করে () যে কোনও জায়গায় এটির রেফারেন্স সঞ্চয় করে ওভাররাইড করে কোনও বস্তুকে পুনরুত্থিত করতে পারেন। আবর্জনা সংগ্রহকারীকে কেবল একবার বস্তুর উপরে ডাকা হবে যাতে এর পরে বস্তুটি কখনই ধ্বংস হয় না।
finalize()
ডাকা হবে না তবে একবারে আরও রেফারেন্স পাওয়া যাবে না বলে এই বস্তুটি সংগ্রহ করা হবে। আবর্জনা সংগ্রহকারীকে 'বলাও হয় না'।
finalize()
পদ্ধতিটিকে কেবল একবার জেভিএম দ্বারা ডাকা যেতে পারে, তবে এর অর্থ এই নয় যে বস্তুটি পুনরুত্থিত করা হয় এবং তারপরে আবার অবজ্ঞা করা হয় তবে এটি পুনরায় আবর্জনা সংগ্রহ করা যাবে না। যদি পদ্ধতিতে রিসোর্স ক্লোজিং কোড থাকে finalize()
তবে এই কোডটি আবার চলবে না, এটি মেমরি ফাঁস হতে পারে।
আমি সম্প্রতি আরও সূক্ষ্ম ধরণের রিসোর্স লিকটি পেয়েছি। আমরা শ্রেণি লোডার এর getResourceAsStream এর মাধ্যমে সংস্থানগুলি খুলি এবং এটি ঘটেছিল যে ইনপুট স্ট্রিম হ্যান্ডলগুলি বন্ধ ছিল না।
আহ, আপনি বলতে পারেন, কি বোকা।
আচ্ছা, এটিকে আকর্ষণীয় করে তোলে: জেভিএমের গাদা না থেকে আপনি অন্তর্নিহিত প্রক্রিয়াটির হিপ মেমরি ফাঁস করতে পারেন।
আপনার যা দরকার তা হ'ল একটি জার ফাইল যা ভিতরে জাভা কোড থেকে রেফারেন্স করা যায় inside বড় জার ফাইলটি, দ্রুত মেমরি বরাদ্দ হয়ে যায়।
আপনি নিম্নলিখিত শ্রেণীর সাহায্যে সহজেই এই জাতীয় জার তৈরি করতে পারেন:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class BigJarCreator {
public static void main(String[] args) throws IOException {
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("big.jar")));
zos.putNextEntry(new ZipEntry("resource.txt"));
zos.write("not too much in here".getBytes());
zos.closeEntry();
zos.putNextEntry(new ZipEntry("largeFile.out"));
for (int i=0 ; i<10000000 ; i++) {
zos.write((int) (Math.round(Math.random()*100)+20));
}
zos.closeEntry();
zos.close();
}
}
বিগজারক্রিটর.জাভা নামে একটি ফাইলের মধ্যে কেবল পেস্ট করুন, এটি কমান্ড লাইন থেকে সংকলন করুন এবং চালনা করুন:
javac BigJarCreator.java
java -cp . BigJarCreator
এবং দেখুন: আপনি ভিতরে দুটি ফাইল সহ আপনার বর্তমান ওয়ার্কিং ডিরেক্টরিতে একটি জার সংরক্ষণাগার খুঁজে পান।
আসুন একটি দ্বিতীয় শ্রেণি তৈরি করুন:
public class MemLeak {
public static void main(String[] args) throws InterruptedException {
int ITERATIONS=100000;
for (int i=0 ; i<ITERATIONS ; i++) {
MemLeak.class.getClassLoader().getResourceAsStream("resource.txt");
}
System.out.println("finished creation of streams, now waiting to be killed");
Thread.sleep(Long.MAX_VALUE);
}
}
এই শ্রেণিটি মূলত কিছুই করে না, তবে নির্বিঘ্নিত ইনপুটস্ট্রিম অবজেক্ট তৈরি করে। এই বিষয়গুলি অবিলম্বে আবর্জনা সংগ্রহ করা হবে এবং এইভাবে, গাদা আকারে অবদান রাখবেন না। একটি জার ফাইল থেকে একটি বিদ্যমান উত্স লোড করা আমাদের উদাহরণের জন্য এটি গুরুত্বপূর্ণ, এবং আকারটি এখানে গুরুত্বপূর্ণ নয়!
যদি আপনার সন্দেহ হয় তবে উপরের ক্লাসটি সংকলন করে শুরু করার চেষ্টা করুন, তবে একটি শালীন গাদা আকার (2 এমবি) চয়ন করতে ভুলবেন না:
javac MemLeak.java
java -Xmx2m -classpath .:big.jar MemLeak
আপনি এখানে কোনও ওওএম ত্রুটির মুখোমুখি হবেন না, কারণ কোনও রেফারেন্স রাখা হয়নি, অ্যাপ্লিকেশনটি চলমান থাকবে যে আপনি উপরের উদাহরণে ITERATIONS কে কতটা বড় চয়ন করেছেন। অ্যাপ্লিকেশন অপেক্ষা কমান্ড না পেলে আপনার প্রক্রিয়াটির মেমরি খরচ (শীর্ষে প্রদর্শিত হবে (আরইএস / আরএসএস) বা প্রক্রিয়া এক্সপ্লোরার) বৃদ্ধি পায় grows উপরের সেটআপে, এটি মেমরির প্রায় 150 এমবি বরাদ্দ করবে।
আপনি যদি অ্যাপ্লিকেশনটি নিরাপদে খেলতে চান তবে ইনপুট স্ট্রিমটি যেখানে তৈরি হয়েছে ঠিক সেখানেই বন্ধ করুন:
MemLeak.class.getClassLoader().getResourceAsStream("resource.txt").close();
এবং আপনার প্রক্রিয়া পুনরাবৃত্তি গণনার চেয়ে পৃথক 35 এমবি ছাড়িয়ে যাবে না।
বেশ সহজ এবং অবাক।
যেহেতু অনেক লোক পরামর্শ দিয়েছে, রিসোর্স লিকগুলি কারণ করা মোটামুটি সহজ - জেডিবিসি উদাহরণগুলির মতো। আসল মেমোরি ফাঁস কিছুটা শক্ত - বিশেষত যদি আপনি JVM এর ভাঙা বিটের উপর নির্ভর না করে থাকেন এটি করার জন্য ...
খুব বড় পদচিহ্ন রয়েছে এমন বস্তু তৈরি করার ধারণা এবং তারপরে সেগুলি অ্যাক্সেস করতে সক্ষম না হওয়ায় আসল মেমরি ফাঁস হয় না। যদি কোনও কিছুই এটিকে অ্যাক্সেস করতে না পারে তবে তা আবর্জনা সংগ্রহ করা হবে এবং যদি কিছু এটি অ্যাক্সেস করতে পারে তবে এটি ফাঁস নয় ...
যদিও একটি উপায় কাজ করত - এবং আমি জানি না এটি এখনও হয় কিনা - একটি তিন-গভীর বিজ্ঞপ্তি শৃঙ্খল আছে। অবজেক্ট এ-তে যেমন অবজেক্ট বি-তে একটি রেফারেন্স রয়েছে, অবজেক্ট বি-তে অবজেক্ট সি-তে একটি রেফারেন্স রয়েছে এবং অবজেক্ট সি-তে অবজেক্ট-এ-তে একটি রেফারেন্স রয়েছে। জিসি এতটা চালাক ছিল যে একটি দুটি গভীর শৃঙ্খল - এ <--> বি হিসাবে - যদি A এবং B অন্য কোনও কিছুর দ্বারা অ্যাক্সেসযোগ্য না হয় তবে সুরক্ষিতভাবে সংগ্রহ করা যেতে পারে, তবে ত্রি-মুখী চেইনটি পরিচালনা করতে পারে না ...
সম্ভাব্য বিশাল মেমরি তথ্য ফাঁসের তৈরি করতে আরেকটি উপায় উল্লেখ রাখা হয় Map.Entry<K,V>
একটি এর TreeMap
।
কেন এটি কেবলমাত্র ক্ষেত্রে প্রযোজ্য তা নির্ধারণ করা শক্ত TreeMap
, তবে বাস্তবায়নটি দেখে কারণটি হতে পারে: TreeMap.Entry
স্টোরগুলি তার ভাইবোনদের জন্য রেফারেন্স দেয়, সুতরাং যদি কোনও TreeMap
সংগ্রহ করার জন্য প্রস্তুত থাকে তবে অন্য কোনও শ্রেণীর যে কোনও একটির রেফারেন্স রয়েছে এটির Map.Entry
পরে পুরো মানচিত্রটি স্মৃতিতে ধরে রাখা হবে।
বাস্তব জীবনের দৃশ্য:
একটি ডিবি ক্যোয়ারী থাকার কথা কল্পনা করুন যা একটি বড় TreeMap
ডেটা কাঠামোকে দেয়। TreeMap
উপাদান সন্নিবেশ ক্রম বজায় রাখার সাথে সাথে লোকেরা সাধারণত ব্যবহার করে ।
public static Map<String, Integer> pseudoQueryDatabase();
যদি ক্যোয়ারিকে প্রচুর বার বলা হয় এবং প্রতিটি প্রশ্নের জন্য (সুতরাং, প্রত্যেকে Map
প্রত্যাবর্তিত হয়ে) আপনি Entry
কোথাও সংরক্ষণ করেন , স্মৃতিটি ক্রমাগত বাড়তে থাকবে।
নিম্নলিখিত র্যাপার শ্রেণি বিবেচনা করুন:
class EntryHolder {
Map.Entry<String, Integer> entry;
EntryHolder(Map.Entry<String, Integer> entry) {
this.entry = entry;
}
}
অ্যাপ্লিকেশন:
public class LeakTest {
private final List<EntryHolder> holdersCache = new ArrayList<>();
private static final int MAP_SIZE = 100_000;
public void run() {
// create 500 entries each holding a reference to an Entry of a TreeMap
IntStream.range(0, 500).forEach(value -> {
// create map
final Map<String, Integer> map = pseudoQueryDatabase();
final int index = new Random().nextInt(MAP_SIZE);
// get random entry from map
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue().equals(index)) {
holdersCache.add(new EntryHolder(entry));
break;
}
}
// to observe behavior in visualvm
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
public static Map<String, Integer> pseudoQueryDatabase() {
final Map<String, Integer> map = new TreeMap<>();
IntStream.range(0, MAP_SIZE).forEach(i -> map.put(String.valueOf(i), i));
return map;
}
public static void main(String[] args) throws Exception {
new LeakTest().run();
}
}
প্রতিটি pseudoQueryDatabase()
কল করার পরে , map
উদাহরণগুলি সংগ্রহের জন্য প্রস্তুত হওয়া উচিত, তবে এটি ঘটবে না, কারণ কমপক্ষে একটি Entry
অন্য কোথাও সঞ্চিত রয়েছে।
আপনার jvm
সেটিংসের উপর নির্ভর করে এ-এর কারণে প্রাথমিক পর্যায়ে অ্যাপ্লিকেশনটি ক্রাশ হতে পারে OutOfMemoryError
।
আপনি এই visualvm
গ্রাফ থেকে দেখতে পারেন কীভাবে স্মৃতিশক্তি বাড়তে থাকে।
হ্যাশড ডেটা স্ট্রাকচার ( HashMap
) এর সাথেও এটি ঘটে না ।
এটি ব্যবহার করার সময় এই গ্রাফটি HashMap
।
সমাধান? এটিকে সংরক্ষণ না করে সরাসরি কী / মানটি (যেমন আপনি সম্ভবত ইতিমধ্যে করেছেন) সংরক্ষণ করুন Map.Entry
।
আমি এখানে আরও বিস্তৃত একটি মানদণ্ড লিখেছি ।
থ্রেডগুলি সমাপ্ত না হওয়া পর্যন্ত সংগ্রহ করা হয় না। তারা আবর্জনা সংগ্রহের শিকড় হিসাবে কাজ করে । এগুলি হ'ল কয়েকটি বস্তুর মধ্যে একটি যা তাদের সম্পর্কে ভুলে যাওয়া বা তাদের উল্লেখগুলি সাফ করার মাধ্যমে পুনরুদ্ধার করা হবে না।
বিবেচনা করুন: একটি কর্মী থ্রেডকে সমাপ্ত করার প্রাথমিক প্যাটার্নটি হ'ল থ্রেডের দ্বারা দেখা কিছু শর্ত পরিবর্তনশীল সেট করা। থ্রেড পর্যায়ক্রমে চলকটি পরীক্ষা করতে পারে এবং এটি সমাপ্তির লক্ষণ হিসাবে ব্যবহার করতে পারে। যদি ভেরিয়েবলটি ঘোষণা না করা হয় volatile
, তবে ভেরিয়েবলের পরিবর্তনটি থ্রেড দ্বারা নাও দেখা যায়, তাই এটি সমাপ্ত হতে জানবে না। বা কল্পনা করুন যে কোনও থ্রেড যদি কোনও ভাগ করা অবজেক্ট আপডেট করতে চায় তবে লক করার চেষ্টা করার সময় ডেডলক।
যদি আপনার হাতে কেবল কয়েকটি থ্রেড থাকে তবে এই বাগগুলি সম্ভবত সুস্পষ্ট হবে কারণ আপনার প্রোগ্রামটি সঠিকভাবে কাজ করা বন্ধ করবে। আপনার যদি এমন একটি থ্রেড পুল রয়েছে যা প্রয়োজনীয় হিসাবে আরও থ্রেড তৈরি করে, তবে অপ্রচলিত / আটকে থ্রেডগুলি লক্ষ্য করা যায় না এবং এটি অনির্দিষ্টকালের জন্য জমা হয়ে যায়, যার ফলে স্মৃতি ফাঁস হয়। থ্রেডগুলি সম্ভবত আপনার অ্যাপ্লিকেশনে অন্যান্য ডেটা ব্যবহার করতে পারে, তাই এগুলি সরাসরি সংগ্রহ করা থেকে সরাসরি যে কোনও কিছু উল্লেখ করে তা প্রতিরোধ করবে।
খেলনার উদাহরণ হিসাবে:
static void leakMe(final Object object) {
new Thread() {
public void run() {
Object o = object;
for (;;) {
try {
sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {}
}
}
}.start();
}
System.gc()
আপনার পছন্দ মতো সকলকে কল করুন , তবে যে বস্তুটি পাস করেছে leakMe
তা কখনও মরে না।
(* সম্পাদিত *)
আমি মনে করি যে বৈধ উদাহরণ থ্রেডলোকাল ভেরিয়েবল এমন পরিবেশে ব্যবহার করতে পারে যেখানে থ্রেডগুলি পুল করা হয়।
উদাহরণস্বরূপ, অন্যান্য ওয়েব উপাদানগুলির সাথে কথোপকথনের জন্য সার্লেটগুলিতে থ্রেডলোকাল ভেরিয়েবলগুলি ব্যবহার করে থ্রেডটি কন্টেইনার দ্বারা তৈরি করা হচ্ছে এবং একটি পুলের মধ্যে নিষ্ক্রিয় রক্ষণাবেক্ষণ করা উচিত। থ্রেডলোকাল ভেরিয়েবলগুলি যদি সঠিকভাবে পরিষ্কার না হয় তবে একই ওয়েব উপাদানটি তাদের মানগুলিকে ওভাররাইট না করা পর্যন্ত সেখানে বেঁচে থাকবে।
অবশ্যই একবার চিহ্নিত হয়ে গেলে সমস্যাটি সহজেই সমাধান করা যায়।
সাক্ষাত্কারকারীর একটি বিজ্ঞপ্তি রেফারেন্স সমাধান খুঁজছেন হতে পারে:
public static void main(String[] args) {
while (true) {
Element first = new Element();
first.next = new Element();
first.next.next = first;
}
}
রেফারেন্স গণনা আবর্জনা সংগ্রহকারীদের সাথে এটি একটি ক্লাসিক সমস্যা। তারপরে আপনি বিনয়ের সাথে ব্যাখ্যা করবেন যে জেভিএমগুলি আরও অনেক পরিশীলিত অ্যালগরিদম ব্যবহার করে যার এই সীমাবদ্ধতা নেই।
-আমাদের টারলে
first
নয় এবং আবর্জনা সংগ্রহ করা উচিত। ইন রেফারেন্স কাউন্টিং আবর্জনা সংগ্রাহক, বস্তুর freeed হবে না (নিজে) এটা একটি সক্রিয় রেফারেন্সে না থাকায়। অসীম লুপটি এখানে ফুটোটি প্রকাশ করার জন্য: আপনি যখন প্রোগ্রামটি চালাবেন, তখন স্মৃতিটি অনির্দিষ্টকালের জন্য বাড়বে।