كيف يمكنني تخزين الكائنات مؤقتًا بكفاءة في Java باستخدام ذاكرة الوصول العشوائي المتوفرة؟

StackOverflow https://stackoverflow.com/questions/2158868

سؤال

أحتاج إلى تخزين الكائنات مؤقتًا في Java باستخدام نسبة من ذاكرة الوصول العشوائي المتوفرة.أدرك أن الآخرين قد طرحوا هذا السؤال، ولكن لا تفي أي من الإجابات بمتطلباتي.

متطلباتي هي:

  • بسيطة وخفيفة الوزن
  • ليس أبطأ بشكل كبير من HashMap العادي
  • استخدم LRU، أو بعض سياسات الحذف التي تقارب LRU

لقد جربت LinkedHashMap، ولكنه يتطلب منك تحديد الحد الأقصى لعدد العناصر، ولا أعرف عدد العناصر التي ستستغرقها لملء ذاكرة الوصول العشوائي المتوفرة (ستختلف أحجامها بشكل كبير).

أسلوبي الحالي هو استخدام MapMaker الخاص بمجموعة Google على النحو التالي:

Map<String, Object> cache = new MapMaker().softKeys().makeMap();

بدا هذا جذابًا لأنه يجب أن يقوم بحذف العناصر تلقائيًا عندما يحتاج إلى المزيد من ذاكرة الوصول العشوائي، ولكن هناك مشكلة خطيرة:يتمثل سلوكه في ملء كل ذاكرة الوصول العشوائي (RAM) المتوفرة، وعند هذه النقطة يبدأ GC في الانهيار ويتدهور أداء التطبيق بأكمله بشكل كبير.

لقد سمعت عن أشياء مثل EHCache، لكنها تبدو ثقيلة جدًا بالنسبة لما أحتاج إليه، ولست متأكدًا مما إذا كانت سريعة بما يكفي لتطبيقي (تذكر أن الحل لا يمكن أن يكون أبطأ بشكل كبير من HashMap) .

هل كانت مفيدة؟

المحلول

لدي متطلبات مماثلة لك - التزامن (على وحدتي معالجة مركزية سداسية النواة) وLRU أو ما شابه - وقمت أيضًا بتجربة Guava MapMaker.لقد وجدت softValues() أبطأ بكثير من WeakValues()، لكن كلاهما جعل تطبيقي بطيئًا بشكل مؤلم عند امتلاء الذاكرة.

لقد جربت WeakHashMap وكان أقل إشكالية، ومن الغريب أنه أسرع من استخدام LinkedHashMap كذاكرة تخزين مؤقت LRU عبر طريقة RemoveEldestEntry() الخاصة به.

ولكن الأسرع بالنسبة لي هو المتزامنLinkedHashMap مما جعل تطبيقي أسرع بـ 3-4 مرات (!!) من أي ذاكرة تخزين مؤقت أخرى قمت بتجربتها.الفرح بعد أيام من الإحباط!يبدو أنه تم دمجها في MapMaker الخاص بـ Guava، لكن ميزة LRU غير موجودة في Guava r07 على أي حال.آمل أن يعمل لك.

نصائح أخرى

لقد قمت بتنفيذ ذاكرة التخزين المؤقت للخدمة وربما يكون من الصعب تنفيذ مصدر بيانات جديد أو ThreadPool ، توصيتي هي استخدام JBOSS-Cache أو LIB للتخزين المؤقت المعروف جيدًا. لذلك سوف تنام بشكل جيد بدون مشاكل

لقد سمعت عن أشياء مثل Ehcache ، لكن يبدو ذلك ثقيلًا جدًا لما أحتاجه ، ولست متأكدًا مما إذا كان ذلك سريعًا بما يكفي لتطبيقي (تذكر أن الحل لا يمكن أن يكون أبطأ بشكل كبير من HashMap) .

أنا حقًا لا أعرف ما إذا كان يمكن للمرء أن يقول ذلك ehcache ثقيل الوزن. على الأقل ، لا أعتبر ehcache على هذا النحو ، خاصة عند استخدام أ متجر الذاكرة (الذي يدعمه موسع LinkedHashMap وهو بالطبع خيار التخزين المؤقت أسرع). يجب عليك محاولة إعطائها.

أعتقد MapMaker ستكون الطريقة الوحيدة المعقولة للحصول على ما تطلبه. إذا كان "GC يبدأ في السحق وتدهور أداء التطبيق بأكمله بشكل كبير" ، فيجب عليك قضاء بعض الوقت بشكل صحيح في تعيين معلمات التوليف المختلفة. قد يبدو هذا المستند مخيفًا بعض الشيء في البداية ، لكنه مكتوب بشكل واضح بالفعل وهو منجم ذهبي من المعلومات المفيدة حول GC:

http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf

لا أعرف ما إذا كان هذا سيكون حلاً بسيطًا ، خاصة مقارنة بـ Ehcache أو ما شابه ، لكن هل نظرت إلى مكتبة Javolution؟ إنه غير مصمم على هذا النحو ، ولكن في javolution.context الحزمة لديهم نمط مخصص يمكنه إعادة استخدام الكائنات دون الحاجة إلى جمع القمامة. وبهذه الطريقة ، يحتفظون بإنشاء الكائنات وجمع القمامة إلى الحد الأدنى ، وهي ميزة مهمة للبرمجة في الوقت الفعلي. ربما يجب عليك إلقاء نظرة ومحاولة تكييفها مع مشكلتك.

بدا هذا جذابًا لأنه ينبغي حذف العناصر تلقائيًا عندما يحتاج إلى مزيد من ذاكرة الوصول العشوائي ، ولكن هناك مشكلة خطيرة: سلوكها هو ملء جميع ذاكرة الوصول العشوائي المتاحة

إن استخدام المفاتيح اللينة يتيح فقط لمجمع القمامة إزالة الكائنات من ذاكرة التخزين المؤقت عندما لا تشير إليها أي كائنات أخرى (أي ، عندما يكون الشيء الوحيد الذي يشير إلى مفتاح ذاكرة التخزين المؤقت هو ذاكرة التخزين المؤقت نفسه). لا يضمن أي نوع آخر من الطرد.

ستتم إضافة معظم الحلول التي تجدها في أعلى فصول خريطة Java ، بما في ذلك Ehcache.

هل نظرت إلى توصيل المشاعات lrumap؟

لاحظ أن هناك قضية مفتوحة ضد صانع الخطر لتوفير وظائف LRU/MRU. ربما يمكنك التعبير عن رأيك هناك أيضًا

باستخدام ذاكرة التخزين المؤقت الحالية ، تخزين الضعف بدلاً من إحالة الكائنات العادية.

إذا بدأت GC في نفاد المساحة الحرة ، فسيتم إصدار القيم التي تحتفظ بها DefentReferences.

في الماضي استخدمت JCS. يمكنك إعداد ترتيب لمحاولة مقابلتك. لست متأكدًا مما إذا كان هذا سيفي بجميع متطلباتك/احتياجاتك ، لكنني وجدت أنه قوي جدًا عندما استخدمته.

لا يمكنك "حذف العناصر" ، يمكنك فقط التوقف عن الرجوع إليهم وانتظار GC لتنظيفها ، لذا استمر في مجموعات Google ...

لست على علم بوجود طريقة سهلة لمعرفة حجم الكائن في Java.لذلك، لا أعتقد أنك ستجد طريقة لتقييد بنية البيانات بمقدار ذاكرة الوصول العشوائي (RAM) التي تستهلكها.

بناءً على هذا الافتراض، فأنت عالق في تقييده بعدد الكائنات المخزنة مؤقتًا.أقترح إجراء عمليات محاكاة لعدد قليل من سيناريوهات الاستخدام الواقعية وجمع الإحصائيات حول أنواع الكائنات التي تدخل في ذاكرة التخزين المؤقت.ثم يمكنك حساب متوسط ​​الحجم إحصائيًا، وعدد الكائنات التي يمكنك تخزينها مؤقتًا.على الرغم من أن هذا مجرد تقدير تقريبي لمقدار ذاكرة الوصول العشوائي (RAM) التي تريد تخصيصها لذاكرة التخزين المؤقت، إلا أنها قد تكون جيدة بما فيه الكفاية.

فيما يتعلق بتنفيذ ذاكرة التخزين المؤقت، في مشروعي (تطبيق مهم للأداء) نستخدم EhCache، وأنا شخصيًا لا أجده ذا وزن ثقيل على الإطلاق.

على أية حال، قم بإجراء العديد من الاختبارات مع العديد من التكوينات المختلفة (فيما يتعلق بالحجم وسياسة الإخلاء وما إلى ذلك) واكتشف ما هو الأفضل بالنسبة لك.

التخزين المؤقت لشيء ما ، SoftReference ربما أفضل طريقة حتى الآن أستطيع أن أتخيل.

أو يمكنك إعادة اختراع مجد الكائن. أن كل كائن لا تستخدمه ، لا تحتاج إلى تدميره. ولكن لحفظ وحدة المعالجة المركزية بدلاً من حفظ الذاكرة

على افتراض أنك تريد أن تكون ذاكرة التخزين المؤقت آمنة للخيط ، يجب عليك فحص مثال ذاكرة التخزين المؤقت في كتاب براين جويتز "Java Concurrency in Practice". لا أستطيع أن أوصي بهذا بدرجة كافية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top