কেন আবর্জনা সংগ্রহ কেবল স্মৃতিতে বিস্তৃত হয় এবং অন্যান্য সংস্থার ধরণ নয়?


12

দেখে মনে হচ্ছে লোকেরা ম্যানুয়াল মেমোরি পরিচালনায় ক্লান্ত হয়ে পড়েছিল, তাই তারা আবর্জনা সংগ্রহের আবিষ্কার করেছিল এবং জীবন যথেষ্ট ভাল ছিল was তবে অন্যান্য প্রতিটি সংস্থান সম্পর্কে কি? ফাইল বর্ণনাকারী, সকেট, বা এমনকি ব্যবহারকারী ডেটাবেস সংযোগের মতো ডেটা তৈরি করেছেন?

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

  1. এটি প্রায় শেষ হতে চলেছে না তা নিশ্চিত হয়ে দেখুন।
  2. যদি তা হয় তবে আবর্জনা সংগ্রহকারীকে ট্রিগার করুন, যা একগুচ্ছ স্মৃতি মুক্ত করবে।
  3. যদি মেমরির কিছু মুক্ত হয় তবে ফাইল বর্ণনাকারীর রেফারেন্স থাকলে তা তাৎক্ষণিকভাবে বন্ধ করুন। এটি জানে যে মেমোরিটি কোনও সংস্থার অন্তর্গত কারণ কারণ সেই সংস্থার সাথে যুক্ত মেমরিটি 'ফাইল বর্ণনাকারী রেজিস্ট্রি'তে নিবন্ধিত হয়েছিল, যখন এটি প্রথম খোলার আগে আরও ভাল শর্তের অভাবে ছিল।
  4. একটি নতুন ফাইল বর্ণনাকারী খুলুন, এটিকে নতুন মেমরিতে অনুলিপি করুন, সেই মেমরির অবস্থানটি 'ফাইল বর্ণনাকারী রেজিস্ট্রি'তে নিবন্ধ করুন এবং এটি ব্যবহারকারীর কাছে ফিরিয়ে দিন।

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

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

উত্তর:


4

জিসি অনুমানযোগ্য এবং সংরক্ষিত সংস্থান নিয়ে কাজ করে। ভিএম এর উপর সম্পূর্ণ নিয়ন্ত্রণ রয়েছে এবং কোন ঘটনাগুলি কখন এবং কখন তৈরি হয় তার সম্পূর্ণ নিয়ন্ত্রণ রয়েছে। এখানে কীওয়ার্ডগুলি "সংরক্ষিত" এবং "সম্পূর্ণ নিয়ন্ত্রণ"। হ্যান্ডলগুলি ওএস দ্বারা বরাদ্দ করা হয়, এবং পয়েন্টারগুলি হ'ল ... পরিচালিত জায়গার বাইরে বরাদ্দকৃত সংস্থানগুলিতে ভাল পয়েন্টার। সে কারণে হ্যান্ডলস এবং পয়েন্টারগুলি পরিচালিত কোডের অভ্যন্তরে ব্যবহারের মধ্যে সীমাবদ্ধ নয়। এগুলি একই প্রক্রিয়াটিতে পরিচালিত এবং পরিচালনা না করে পরিচালনা করা কোড দ্বারা এবং ব্যবহার করা যেতে পারে often

একজন "রিসোর্স কালেক্টর" কোনও হ্যান্ডেল / পয়েন্টারটি কোনও পরিচালিত জায়গার মধ্যে ব্যবহার করা হচ্ছে কিনা তা যাচাই করতে সক্ষম হবে তবে এটি তার স্মৃতি স্থানের বাইরে কী ঘটছে তা সংজ্ঞায়িত নয় (এবং বিষয়গুলি আরও খারাপ করে তুলতে কিছু হ্যান্ডেল ব্যবহার করা যেতে পারে প্রক্রিয়া সীমানা জুড়ে)।

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

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

সম্পাদনা 2: এটি কেবলমাত্র পরিচালিত জায়গার অভ্যন্তরে ব্যবহার করা হলে নির্দিষ্ট হ্যান্ডেলটি মুক্ত করার জন্য কিছু কোড লিখতে সিএলআর / জেভিএম / ভিএমএস-ইন-জেনারেলের তুলনামূলকভাবে তুচ্ছ। নেট মধ্যে এমন কিছু হবে:

// This class offends many best practices, but it would do the job.
public class AutoReleaseFileHandle {
    // keeps track of how many instances of this class is in memory
    private static int _toBeReleased = 0;

    // the threshold when a garbage collection should be forced
    private const int MAX_FILES = 100;

    public AutoReleaseFileHandle(FileStream fileStream) {
       // Force garbage collection if max files are reached.
       if (_toBeReleased >= MAX_FILES) {
          GC.Collect();
       }
       // increment counter
       Interlocked.Increment(ref _toBeReleased);
       FileStream = fileStream;
    }

    public FileStream { get; private set; }

    private void ReleaseFileStream(FileStream fs) {
       // decrement counter
       Interlocked.Decrement(ref _toBeReleased);
       FileStream.Close();
       FileStream.Dispose();
       FileStream = null;
    }

    // Close and Dispose the Stream when this class is collected by the GC.
    ~AutoReleaseFileHandle() {
       ReleaseFileStream(FileStream);
    }

    // because it's .NET this class should also implement IDisposable
    // to allow the user to dispose the resources imperatively if s/he wants 
    // to.
    private bool _disposed = false;
    public void Dispose() {
      if (_disposed) {
        return;
      }
      _disposed = true;
      // tells GC to not call the finalizer for this instance.
      GC.SupressFinalizer(this);

      ReleaseFileStream(FileStream);
    }
}

// use it
// for it to work, fs.Dispose() should not be called directly,
var fs = File.Open("path/to/file"); 
var autoRelease = new AutoReleaseFileHandle(fs);

3

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

ফাইনালাইজার ব্যবহারের জন্য এখানে বেশ ভাল লেখা রয়েছে:

অবজেক্ট চূড়ান্তকরণ এবং পরিষ্কার

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


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

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

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

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

3

এই ধরণের সংস্থানগুলি পরিচালনা করতে অনেক প্রোগ্রামিং কৌশল রয়েছে।

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

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

  • সি # এবং অন্যান্য ভাষাগুলি এমন একটি usingকীওয়ার্ড সরবরাহ করে যা এটির প্রয়োজন পরে আর সংস্থানগুলি বন্ধ হয়ে যায় তা নিশ্চিত করতে সহায়তা করে (যাতে আপনি ফাইল বর্ণনাকারী বা অন্যান্য সংস্থান বন্ধ করতে ভুলবেন না)। এটি আবর্জনা সংগ্রাহকের উপর নির্ভর করার চেয়ে এই জিনিসটি আর বেঁচে নেই তা প্রায়শই ভাল। দেখুন, যেমন, https://stackoverflow.com/q/75401/781723 । এখানে সাধারণ শব্দটি একটি পরিচালিত সংস্থান । এই ধারণাটি RAII এবং চূড়ান্তকরণগুলির উপর ভিত্তি করে কিছু দিক থেকে তাদের উন্নতি করে on


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

2

সমস্ত স্মৃতি সমান, যদি আমি 1 কে জিজ্ঞাসা করি, তবে ঠিকানা স্পেসে 1K কোথা থেকে আসে সেদিকে আমার খেয়াল নেই।

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

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

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


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

0

আমি অনুমান করব যে অন্যান্য সংস্থাগুলির জন্য কেন এটির কাছে এত বেশি যোগাযোগ করা হয়নি ঠিক এটি কারণ যে বেশিরভাগ অন্যান্য সংস্থান যে কেউ পুনরায় ব্যবহারের জন্য যত তাড়াতাড়ি সম্ভব মুক্তি দেওয়া পছন্দ করে।

দ্রষ্টব্য, অবশ্যই, আপনার বিদ্যমান উদাহরণটি এখন বিদ্যমান জিসি কৌশলযুক্ত "দুর্বল" ফাইল বর্ণনাকারী ব্যবহার করে সরবরাহ করা যেতে পারে।


0

মেমোরিটি আর অ্যাক্সেসযোগ্য নয় কিনা তা পরীক্ষা করা (এবং এভাবে আর ব্যবহারের নিশ্চয়তা নেই) বরং সহজ। অন্যান্য অন্যান্য ধরণের সংস্থানগুলি কম-বেশি একই কৌশলগুলি দ্বারা পরিচালনা করা যেতে পারে (অর্থাত্, উত্স অধিগ্রহণটি ইনিশিয়েশন, আরআইআইআই এবং ব্যবহারকারী ধ্বংস হয়ে যাওয়ার পরে এটি মুক্ত করার সমকক্ষ হয়, যা এটিকে স্মৃতি প্রশাসনের সাথে যুক্ত করে)। কিছুটা "ঠিক সময়ে" বিনামূল্যে করা সাধারণভাবে অসম্ভব (থামার সমস্যাটি পরীক্ষা করে দেখুন, আপনাকে খুঁজে বার করতে হবে যে কিছু সংস্থান শেষ বারের জন্য ব্যবহৃত হয়েছিল)। হ্যাঁ, কখনও কখনও এটি স্বয়ংক্রিয়ভাবে করা যেতে পারে তবে এটি স্মৃতি হিসাবে অনেক মেসিরযুক্ত ঘটনা। সুতরাং এটি বেশিরভাগ অংশের জন্য ব্যবহারকারীর হস্তক্ষেপের উপর নির্ভর করে।

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