জাভাস্ক্রিপ্ট বন্ধের জঞ্জাল কীভাবে সংগ্রহ করা হয়


168

আমি নিম্নলিখিত ক্রোম বাগটি লগইন করেছি , যা আমার কোডে অনেক গুরুতর এবং স্পষ্টতই মেমরি ফাঁস না করে:

(এই ফলাফলগুলি ক্রোম দেব সরঞ্জামগুলির মেমরি প্রোফাইলার ব্যবহার করে , যা জিসি চালায় এবং তারপরে আবর্জনা সংগ্রহ না করা সমস্ত কিছুর স্ন্যাপশট নেয়))

নীচের someClassকোডে উদাহরণটি হ'ল আবর্জনা সংগ্রহ করা (ভাল):

var someClass = function() {};

function f() {
  var some = new someClass();
  return function() {};
}

window.f_ = f();

তবে এটি এই ক্ষেত্রে আবর্জনা সংগ্রহ করা হবে না (খারাপ):

var someClass = function() {};

function f() {
  var some = new someClass();
  function unreachable() { some; }
  return function() {};
}

window.f_ = f();

এবং সম্পর্কিত স্ক্রিনশট:

Chromebug এর স্ক্রিনশট

দেখে মনে হচ্ছে যে কোনও অবসান (এই ক্ষেত্রে function() {}) সমস্ত অবজেক্টকে "জীবিত" রাখে যদি বস্তুটি অন্য কোনও বন্ধ দ্বারা একই প্রসঙ্গে রেফারেন্স করা হয়, সেই ক্লোজারটি নিজেই পৌঁছনীয় কিনা।

আমার প্রশ্নটি অন্যান্য ব্রাউজারগুলিতে (IE 9+ এবং ফায়ারফক্স) বন্ধের আবর্জনা সংগ্রহ সম্পর্কে। আমি জাভাস্ক্রিপ্ট হিপ প্রোফাইলারের মতো ওয়েবকিটের সরঞ্জামগুলির সাথে বেশ পরিচিত, তবে আমি অন্যান্য ব্রাউজারগুলির সরঞ্জামগুলি খুব কম জানি, তাই আমি এটি পরীক্ষা করতে সক্ষম হইনি।

এই তিনটি ক্ষেত্রে কোনটি আই 9+ এবং ফায়ারফক্সের আবর্জনা সংগ্রহ করবে someClass ?


4
নিরবিচ্ছিন্ন করার জন্য, ক্রোম কীভাবে আপনাকে পরীক্ষা করতে দেয় যে কোন ভেরিয়েবল / অবজেক্টগুলি আবর্জনা সংগ্রহ করা হয় এবং কখন ঘটে?
nnnnnn

1
হতে পারে কনসোল এটিতে একটি রেফারেন্স রাখছে। আপনি যখন কনসোলটি সাফ করবেন তখন কি এটি জিসিড হয়?
ডেভিড

1
@ ডেভিড শেষ উদাহরণে unreachableফাংশনটি কখনই কার্যকর হয় না তাই আসলে কিছুই লগ হয় না।
জেমস মন্টাগন

1
আমাদের যদি সত্যের মুখোমুখি করা হয় বলে মনে হয় তবেও সেই গুরুত্বের একটি ত্রুটিটি পেরেছে তা বিশ্বাস করতে আমার সমস্যা হচ্ছে। তবে আমি কোডটি বারবার দেখছি এবং আমি অন্য কোনও যুক্তিযুক্ত ব্যাখ্যা খুঁজে পাই না। আপনি ঠিক এখনই কনসোলে কোডটি চালাবার চেষ্টা করলেন না (ওরফে ব্রাউজারটি কোনও বোঝা স্ক্রিপ্ট থেকে প্রাকৃতিকভাবে এটি চালাতে দেয়)?
প্ল্লেক্স

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

উত্তর:


78

আমি যতদূর বলতে পারি এটি কোনও বাগ নয়, প্রত্যাশিত আচরণ।

মজিলার মেমোরি পরিচালনা পৃষ্ঠা থেকে : "২০১২ সালের মতো, সমস্ত আধুনিক ব্রাউজারগুলি একটি চিহ্ন-ও-জঞ্জাল সংগ্রহকারী সংগ্রহ করে" " "সীমাবদ্ধতা: অবজেক্টগুলিকে সুস্পষ্টভাবে পৌঁছনীয় নয় "

আপনার উদাহরণগুলিতে যেখানে এটি ব্যর্থ someহয় তা বন্ধের মধ্যে এখনও পৌঁছনীয়। আমি এটিকে অ্যাক্সেসযোগ্য এবং উভয়ই কাজ করতে দুটি উপায় চেষ্টা করেছি। হয় আপনি সেট করার some=nullদরকার নেই যখন আপনার আর প্রয়োজন হয় না, বা আপনি সেট করেন window.f_ = null;এবং এটি চলে যাবে।

হালনাগাদ

আমি এটি উইন্ডোজের ক্রোম 30, এফএফ 25, অপেরা 12 এবং আই 10 তে চেষ্টা করেছি।

মান গার্বেজ কালেকশন সম্পর্কে কিছু বলে না, কিন্তু কি হবে কিছু সংকেত সনাক্ত করুন দেয়।

  • অনুচ্ছেদ 13 ফাংশন সংজ্ঞা, পদক্ষেপ 4: "বন্ধ হ'ল 13.2 তে উল্লিখিত হিসাবে একটি নতুন ফাংশন অবজেক্ট তৈরির ফলাফল হোক"
  • অনুচ্ছেদ ১৩.২ "স্কোপ দ্বারা নির্দিষ্ট একটি লেক্সিকাল পরিবেশ" (সুযোগ = বন্ধ)
  • বিভাগ 10.2 লেক্সিকাল পরিবেশ:

"একটি (অভ্যন্তরীণ) লেক্সিকাল এনভায়রনমেন্টের বাইরের রেফারেন্স হ'ল লেজিকাল এনভায়রনমেন্টের একটি রেফারেন্স যা যৌক্তিকভাবে অভ্যন্তরীণ লেক্সিকাল এনভায়রনমেন্টকে ঘিরে থাকে।

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

সুতরাং, কোনও ফাংশনের পিতামাতার পরিবেশে অ্যাক্সেস থাকবে।

সুতরাং, someরিটার্নিং ফাংশন বন্ধ থাকাতে পাওয়া উচিত।

তাহলে কেন সবসময় পাওয়া যায় না?

দেখে মনে হচ্ছে ক্রোম এবং এফএফ কিছু ক্ষেত্রে ভেরিয়েবলটি মুছে ফেলার জন্য যথেষ্ট স্মার্ট, তবে অপেরা এবং আইই উভয় ক্ষেত্রে এই someপরিবর্তনটি বন্ধের মধ্যে উপলব্ধ (এনবি: এই সেটটিকে একটি ব্রেকপয়েন্টটি দেখতে return nullএবং ডিবাগারটি পরীক্ষা করতে)।

someফাংশনগুলিতে ব্যবহৃত হয় কি না তা সনাক্ত করার জন্য জিসি উন্নত হতে পারে তবে এটি জটিল হবে।

একটি খারাপ উদাহরণ:

var someClass = function() {};

function f() {
  var some = new someClass();
  return function(code) {
    console.log(eval(code));
  };
}

window.f_ = f();
window.f_('some');

উদাহরণস্বরূপ, জিসির ভেরিয়েবলটি ব্যবহৃত হয়েছে কি না তা জানার কোনও উপায় নেই (কোড পরীক্ষা করা হয়েছে এবং Chrome30, FF25, অপেরা 12 এবং আই 10 তে কাজ করে)।

মেমরিটি প্রকাশ করা হয় যদি বস্তুর রেফারেন্সটি অন্য মান নির্ধারণ করে ভেঙে যায় window.f_

আমার মতে এটি কোনও বাগ নয়।


4
তবে, একবার setTimeout()কলব্যাক চলার পরে কলব্যাকের সেই ফাংশন স্কোপটি setTimeout()শেষ হয়ে যায় এবং সেই পুরো সুযোগটি আবর্জনা সংগ্রহ করা উচিত, এর উল্লেখটি প্রকাশ করে somesomeবন্ধ করার উদাহরণে পৌঁছতে পারে এমন কোনও কোড আর চলতে পারে না । এটি আবর্জনা সংগ্রহ করা উচিত। শেষ উদাহরণটি আরও খারাপ কারণ unreachable()এটি এমনকি বলা হয় না এবং কারও কাছে এটির উল্লেখ নেই। এর ব্যাপ্তিও জিসিডি হওয়া উচিত। এই উভয় বাগ মত মনে হচ্ছে। কোনও কার্যকারিতার সুযোগে জিনিসগুলিকে "ফ্রি" করতে জেএস-তে কোনও ভাষার প্রয়োজন নেই।
jਫਰ00

1
@ কিছু এটা করা উচিত নয়। ফাংশনগুলি অভ্যন্তরীণভাবে ব্যবহার করছে না এমন ভেরিয়েবলগুলি বন্ধ করার কথা।
প্ল্লেক্স

2
এটি খালি ফাংশন দ্বারা অ্যাক্সেস করা যেতে পারে, তবে এটি এর কোনও প্রকৃত উল্লেখ নেই তাই এটি পরিষ্কার হওয়া উচিত। আবর্জনা সংগ্রহ প্রকৃত উল্লেখগুলির উপর নজর রাখে। এটি রেফারেন্স করা যেতে পারে এমন সমস্ত কিছু ধরে রাখার কথা নয়, কেবলমাত্র এমন জিনিস যা আসলে উল্লেখ করা হয়। শেষ f()বলা হয়ে গেলে আর কোনও প্রকৃত উল্লেখ পাওয়া যায়নি some। এটি পৌঁছনীয় নয় এবং এটি জিসিইড হওয়া উচিত।
jਫਰ00

1
@ jفر0000 আমি (মানক) [ বাস্তু-ইন্টারন্যাশনাল.org / প্রজাতন্ত্র / ফাইলস / ইসিএএমএসটি / এ্যাকমা-262.pdf] তে কিছুই খুঁজে পাচ্ছি না যা কেবল অভ্যন্তরীণভাবে ব্যবহার করে এমন ভেরিয়েবলগুলি সম্পর্কে কিছু পাওয়া উচিত। বিভাগে ১৩, উত্পাদনের পদক্ষেপ 4: 13.3 , 10.2-তে উল্লিখিত হিসাবে একটি নতুন ফাংশন অবজেক্ট তৈরির ফলাফলটি বন্ধ হয়ে যাওয়া যাক "বাহ্যিক পরিবেশের রেফারেন্সটি লেক্সিকাল পরিবেশগত মানগুলির লজিক্যাল নেস্টিংয়ের মডেল হিসাবে ব্যবহৃত হয়। ) লেক্সিকাল এনভায়রনমেন্ট লেজিকাল এনভায়রনমেন্টের একটি উল্লেখ যা যৌক্তিকভাবে অভ্যন্তরীণ লেক্সিকাল পরিবেশকে ঘিরে। "
কিছু

2
ভাল, evalসত্যিই বিশেষ ক্ষেত্রে। উদাহরণস্বরূপ, evalএলিয়াস করা যায় না ( ডেভেলপার.মোজিলা.আর.ইন- ইউএস / ডকস / ওয়েব / জাভা স্ক্রিপ্ট / রেফারেন্স / )), যেমন var eval2 = eval। যদি evalএটি ব্যবহার করা হয় (এবং যেহেতু এটি কোনও আলাদা নামে ডাকা যায় না, এটি করা সহজ) তবে আমাদের অবশ্যই ধরে নিতে হবে যে এটি কোনও সুযোগে ব্যবহার করতে পারে।
পল ড্রাগন

49

আমি এটি IE9 + এবং ফায়ারফক্সে পরীক্ষা করেছি।

function f() {
  var some = [];
  while(some.length < 1e6) {
    some.push(some.length);
  }
  function g() { some; } //removing this fixes a massive memory leak
  return function() {};   //or removing this
}

var a = [];
var interval = setInterval(function() {
  var len = a.push(f());
  if(len >= 500) {
    clearInterval(interval);
  }
}, 10);

লাইভ সাইটে এখানে

আমি function() {}ন্যূনতম মেমরিটি ব্যবহার করে 500 এর অ্যারে দিয়ে বাতাসের আশা করব ।

দুর্ভাগ্যবশত, যে ক্ষেত্রে ছিল না। প্রতিটি খালি ফাংশন একটি মিলিয়ন সংখ্যার (চিরতরে পৌঁছনীয় নয়, তবে GC'ed নয়) অ্যারে ধরে রাখে।

ক্রোম অবশেষে থামে এবং মারা যায়, ফায়ারফক্স প্রায় 4 জিবি র‌্যাম ব্যবহারের পরে পুরো জিনিসটি শেষ করে, এবং আই এম "স্মৃতি ছাড়াই" অব্যাহতভাবে ধীরে ধীরে ধীরে ধীরে বেড়ে যায়।

মন্তব্য করা রেখাগুলির মধ্যে একটি অপসারণ সবকিছু ঠিক করে দেয়।

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

যদি কোনও ক্লোজার রেফারেন্সিং দরকার হয় some(তবে আমি এটি এখানে ব্যবহার করি নি তবে ধারণা করুন যে আমি এটি করেছি), পরিবর্তে যদি হয়

function g() { some; }

আমি ব্যবহার করি

var g = (function(some) { return function() { some; }; )(some);

এটি ক্লোজারটি আমার অন্যান্য ক্রিয়াকলাপের চেয়ে আলাদা প্রসঙ্গে সরিয়ে দিয়ে স্মৃতি সমস্যাগুলি সমাধান করবে।

এটি আমার জীবনকে আরও ক্লান্তিকর করে তুলবে।

পিএস কৌতূহলের বাইরে, আমি জাভাতে এটি চেষ্টা করেছি (ক্রিয়াকলাপগুলির ভিতরে শ্রেণিগুলি সংজ্ঞায়িত করার ক্ষমতাটি ব্যবহার করে)। আমি মূলত জাভাস্ক্রিপ্টের জন্য আশা করেছিলাম তেমন কাজ করে G


আমি মনে করি বাহ্যিক ফাংশন var g = (ফাংশন (কিছু) {রিটার্ন ফাংশন () {কিছু;};}) (কিছু);
এইচসিজে

15

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

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

তারা আসলে কী বন্ধ হয়ে যায় তার উপর নির্ভর করে বিভিন্ন বন্ধের জন্য একাধিক পরিবেশের রেকর্ড তৈরি করার চেষ্টা করতে পারে, তবে এটি খুব দ্রুত জটিল হয়ে উঠতে পারে এবং এর নিজস্ব কার্যকারিতা এবং স্মৃতি সমস্যার কারণ হতে পারে ...


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

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

রেকর্ডের সংখ্যা তৈরি হওয়া সমাপ্তির সংখ্যার সমান। আমি বর্ণনা O(n^2)বা O(2^n)বিস্ফোরণ হিসাবে বর্ণনা করতে পারি , তবে আনুপাতিক বৃদ্ধি নয়।
পল ড্রাগন

ঠিক আছে, ও (এন) ও (1) এর তুলনায় একটি বিস্ফোরণ, বিশেষত যখন প্রত্যেকেই মোটামুটি পরিমাণ স্মৃতি গ্রহণ করতে পারে ... আবার, আমি এ বিষয়ে বিশেষজ্ঞ নই; irc.mozilla.org এ # জাসাপি চ্যানেলটিতে জিজ্ঞাসাবাদ করার ফলে ট্রেড অফগুলি কী কী তা আমি সরবরাহ করতে পারি তার থেকে আপনার আরও ভাল এবং আরও বিশদ ব্যাখ্যা পাওয়ার সম্ভাবনা রয়েছে।
বরিস জবারস্কি

1
দুর্ভাগ্যক্রমে, এসেইলিজা এটি আসলে বেশ সাধারণ। আপনার যা দরকার তা হ'ল ফাংশনে একটি বড় অস্থায়ী (সাধারণত একটি বড় টাইপযুক্ত অ্যারে) যা কিছু এলোমেলো স্বল্প-কালীন কলব্যাক ব্যবহার এবং দীর্ঘকালীন বন্ধ closure এটি সম্প্রতি ওয়েব অ্যাপ লেখার জন্য বেশ কয়েকবার এসেছে ...
বরিস জবারস্কি

0
  1. ফাংশন কলগুলির মধ্যে স্থিতি বজায় রাখুন আসুন আমরা বলি যে আপনার কাছে ফাংশন অ্যাড রয়েছে () এবং আপনি এটির মাধ্যমে পাঠানো সমস্ত মানটি কয়েকটি কলে যুক্ত করতে এবং যোগফলটি ফেরত দিতে চান।

মত অ্যাড (5); // রিটার্ন 5

(20); যোগ করুন // রিটার্ন 25 (5 + 20)

(3); যোগ করুন // ফেরত 28 (25 + 3)

আপনি প্রথমে এটি করতে পারেন এমন দুটি উপায় হ'ল একটি গ্লোবাল ভেরিয়েবলকে সংজ্ঞায়িত করা অবশ্যই, মোটটি ধরে রাখতে আপনি একটি বৈশ্বিক পরিবর্তনশীল ব্যবহার করতে পারেন। তবে মনে রাখবেন যে আপনি যদি (গ) গ্লোবাল ব্যবহার করেন তবে এই ছেলেটি আপনাকে জীবিত খাবে।

ক্লোজার ভেরিয়েবলের সংজ্ঞা দিয়ে এখন ক্লোজার ব্যবহারের সর্বশেষ উপায়

(function(){

  var addFn = function addFn(){

    var total = 0;
    return function(val){
      total += val;
      return total;
    }

  };

  var add = addFn();

  console.log(add(5));
  console.log(add(20));
  console.log(add(3));
  
}());


0

function Country(){
    console.log("makesure country call");	
   return function State(){
   
    var totalstate = 0;	
	
	if(totalstate==0){	
	
	console.log("makesure statecall");	
	return function(val){
      totalstate += val;	 
      console.log("hello:"+totalstate);
	   return totalstate;
    }	
	}else{
	 console.log("hey:"+totalstate);
	}
	 
  };  
};

var CA=Country();
 
 var ST=CA();
 ST(5); //we have add 5 state
 ST(6); //after few year we requare  have add new 6 state so total now 11
 ST(4);  // 15
 
 var CB=Country();
 var STB=CB();
 STB(5); //5
 STB(8); //13
 STB(3);  //16

 var CX=Country;
 var d=Country();
 console.log(CX);  //store as copy of country in CA
 console.log(d);  //store as return in country function in d


দয়া করে উত্তরটি বর্ণনা করুন
janith1024

0

(function(){

   function addFn(){

    var total = 0;
	
	if(total==0){	
	return function(val){
      total += val;	 
      console.log("hello:"+total);
	   return total+9;
    }	
	}else{
	 console.log("hey:"+total);
	}
	 
  };

   var add = addFn();
   console.log(add);  
   

    var r= add(5);  //5
	console.log("r:"+r); //14 
	var r= add(20);  //25
	console.log("r:"+r); //34
	var r= add(10);  //35
	console.log("r:"+r);  //44
	
	
var addB = addFn();
	 var r= addB(6);  //6
	 var r= addB(4);  //10
	  var r= addB(19);  //29
    
  
}());

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.