سؤال

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

في نهاية المطاف أنا أفكر في أن تجعل هذا الكائن a المرجع من java.lang.ref.SoftReference.حتى إذا كان تشغيل النظام منخفض على الذاكرة ، وهذا من شأنه أن يسمح الكائن الذهاب و واحدة جديدة سيتم إنشاؤها على الطلب.هذا من شأنه أن يقلل بعض السرعة بعد بداية جديدة ، ولكن هذا ليس بديلا أفضل من ضرب OOM.

المشكلة الوحيدة أرى عن SoftReferences هو أنه لا يوجد أي وسيلة نظيفة من الحصول على مرجعيات في صيغتها النهائية.في حالتي, قبل تدمير البحث التعامل مع أحتاج إلى إغلاقه ، وإلا فإن النظام قد نفد من واصفات الملفات.من الواضح أنني يمكن أن يلتف هذا المؤشر إلى كائن آخر ، كتابة finalizer على ذلك (أو هوك على ReferenceQueue/PhantomReference) وترك.ولكن كل واحد من المادة في هذا الكوكب تنصح باستخدام finalizers ، وخاصة ضد finalizers من أجل تحرير الملف مقابض (مثلا ، فعالة جافا إد.الثاني, صفحة 27).

لذلك أنا في حيرة بعض الشيء.يجب أن بعناية تجاهل كل هذه النصائح و على الذهاب.وإلا هل هناك أي بدائل قابلة للتطبيق?شكرا مقدما.

تحرير #1:النص أدناه أضيفت بعد اختبار بعض التعليمات البرمجية كما اقترح توم هوتن.بالنسبة لي يبدو أن أي اقتراح لا يعمل أو أنا في عداد المفقودين شيء.هنا كود:

class Bloat {  // just a heap filler really
   private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;

   private final int ii;

   public Bloat(final int ii) {
      this.ii = ii;
   }
}

// as recommended by Tom Hawtin
class MyReference<T> extends SoftReference<T> {
   private final T hardRef;

   MyReference(T referent, ReferenceQueue<? super T> q) {
      super(referent, q);
      this.hardRef = referent;
   }
}

//...meanwhile, somewhere in the neighbouring galaxy...
{
   ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
   Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
   int i=0;

   while(i<50000) {
//      set.add(new MyReference<Bloat>(new Bloat(i), rq));
      set.add(new SoftReference<Bloat>(new Bloat(i), rq));

//      MyReference<Bloat> polled = (MyReference<Bloat>) rq.poll();
      SoftReference<Bloat> polled = (SoftReference<Bloat>) rq.poll();

      if (polled != null) {
         Bloat polledBloat = polled.get();
         if (polledBloat == null) {
           System.out.println("is null :(");
         } else {
           System.out.println("is not null!");
         }
      }
      i++;
   }
}

إذا قمت بتشغيل المقتطف أعلاه ، -Xmx10m و SoftReferences (كما في التعليمات البرمجية أعلاه) ، أنا الحصول على طن من is null :( مطبوعة.ولكن إذا كنت استبدال الرمز مع MyReference (uncommenting خطين مع MyReference وتعليقا على الخروج منها مع SoftReference) أنا دائما الحصول على OOM.

كما فهمت من المشورة ، بعد أن من الصعب الإشارة داخل MyReference لا ينبغي أن يمنع ضرب وجوه ReferenceQueue, صحيح ؟

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

المحلول

وتومز الجواب الصحيح، ولكن التعليمات البرمجية التي قد أضيفت إلى المسألة ليست نفس ما اقترحه توم. ما توم كان اقتراح تبدو أكثر مثل هذا:

class Bloat {  // just a heap filler really
    public Reader res;
    private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;

    private final int ii;

    public Bloat(final int ii, Reader res) {
       this.ii = ii;
       this.res = res;
    }
 }

 // as recommended by Tom Hawtin
 class MySoftBloatReference extends SoftReference<Bloat> {
    public final Reader hardRef;

    MySoftBloatReference(Bloat referent, ReferenceQueue<Bloat> q) {
       super(referent, q);
       this.hardRef = referent.res;
    }
 }

 //...meanwhile, somewhere in the neighbouring galaxy...
 {
    ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
    Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
    int i=0;

    while(i<50000) {
        set.add(new MySoftBloatReference(new Bloat(i, new StringReader("test")), rq));

        MySoftBloatReference polled = (MySoftBloatReference) rq.poll();

        if (polled != null) {
            // close the reference that we are holding on to
            try {
                polled.hardRef.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        i++;
    }
}

لاحظ أن الفارق الكبير هو أن الإشارة الصعبة هو الكائن الذي يحتاج إلى أن تكون مغلقة. الكائن المحيطة يمكن، وسوف، أن القمامة التي تم جمعها، حتى أنك لن تصل إلى OOM، ولكن كنت لا تزال تحصل على فرصة لإغلاق المرجع. بمجرد ترك الحلقة، والتي ستكون أيضا القمامة التي تم جمعها. وبطبيعة الحال، في العالم الحقيقي، وربما كنت لا تجعل res عضوا المثال العام.

ومع ذلك، إذا كنت عقد المراجع فتح الملف، ثم قمت بتشغيل خطر حقيقي جدا من نفاد الذين من قبلكم نفاد الذاكرة. وربما كنت تريد أيضا أن يكون لها مخبأ LRU لضمان أن تحافظ على ما لا يزيد عن العصي الاصبع في الهواء 500 الملفات المفتوحة. هذه يمكن أيضا أن تكون من نوع MyReference بحيث يمكن أيضا تجميع البيانات المهملة إذا لزم الأمر.

لتوضيح قليلا على كيفية عمل MySoftBloatReference، الفئة الأساسية، وهذا هو SoftReference، لا يزال يحمل إشارة إلى الكائن الذي يتم القص جميع من الذاكرة. هذا هو الكائن الذي كنت بحاجة إلى أن تتحرر لمنع OOM من الحدوث. ومع ذلك، إذا يتم تحرير الكائن، لا تزال تحتاج إلى تحرير الموارد التي انفخ تستخدم، وهذا هو، انفخ تستخدم نوعين من الموارد، والذاكرة والتعامل مع الملف، كل من هذه الموارد تحتاج إلى أن تتحرر، أو تشغيل من أصل واحد أو آخر من الموارد. وSoftReference يعالج الضغط على الموارد الذاكرة عن طريق تحرير هذا الكائن، ولكن تحتاج أيضا إلى تحرير الموارد الأخرى، والتعامل مع الملف. لأنه قد تم بالفعل إطلاق سراح انفخ، ونحن لا يمكن استخدامه لتحرير الموارد ذات الصلة، وذلك MySoftBloatReference يحتفظ إشارة ورقية إلى الموارد الداخلية التي تحتاج إلى أن تكون مغلقة. مرة واحدة وقد تم إبلاغها بأن انفخ تم الافراج، أي مرة واحدة في إشارة تتحول حتى في ReferenceQueue، ثم MySoftBloatReference يمكن أيضا إغلاق الموارد ذات الصلة، من خلال الإشارة الصعبة التي لديه.

تعديل: تم تحديث التعليمات البرمجية بحيث يجمع عندما ألقيت في الصف. ويستخدم StringReader لتوضيح مفهوم كيفية إغلاق القارئ، الذي يتم استخدامه لتمثيل الموارد الخارجية التي تحتاج إلى أن تتحرر. في هذه الحالة بالذات إغلاق هذا الدفق هي في الواقع لا مرجع سابق، وذلك ليست هناك حاجة، لكنه يظهر كيفية القيام بذلك إذا لزم الأمر.

نصائح أخرى

لعدد محدود من الموارد: SoftReference الفئة الفرعية. يجب أن يشير المرجع الناعمة إلى كائن إرفاق. إشارة قوية في فئة فرعية يجب أن مرجع الموارد، لذلك هو دائما قابلة للوصول بقوة. عندما تقرأ من خلال ReferenceQueue poll الموارد يمكن أن تكون مغلقة وإزالتها من ذاكرة التخزين المؤقت. يحتاج ذاكرة التخزين المؤقت التي ستصدر بشكل صحيح (إذا كان SoftReference نفسها التي تم جمعها من القمامة، فإنه لا يمكن enqueued الصعود إلى ReferenceQueue).

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

و(جوابي # 1000. أرسلت من لندن DevDay).

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

public class Test1 {
    static class Bloat {  // just a heap filler really
        private double a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;

        private final int ii;

        public Bloat(final int ii) {
            this.ii = ii;
        }
    }

    // as recommended by Tom Hawtin
    static class MyReference<T, K> extends SoftReference<T> {
        private final K keyInformation;

        MyReference(T referent, K keyInformation, ReferenceQueue<? super T> q) {
            super(referent, q);
            this.keyInformation = keyInformation;
        }

        public K getKeyInformation() {
            return keyInformation;
        }
    }

    //...meanwhile, somewhere in the neighbouring galaxy...
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Bloat> rq = new ReferenceQueue<Bloat>();
        Set<SoftReference<Bloat>> set = new HashSet<SoftReference<Bloat>>();
        int i = 0;

        while (i < 50000) {
            set.add(new MyReference<Bloat, Integer>(new Bloat(i), i, rq));

            final Reference<? extends Bloat> polled = rq.poll();

            if (polled != null) {
                if (polled instanceof MyReference) {
                    final Object keyInfo = ((MyReference) polled).getKeyInformation();
                    System.out.println("not null, got key info: " + keyInfo + ", finalizing...");
                } else {
                    System.out.println("null, can't finalize.");
                }
                rq.remove();
                System.out.println("removed reference");
            }

وتحرير:
أريد أن وضع على "إما عقد معلوماتك أو ندعه يذهب". على افتراض انك كان بعض الطريق لعقد لمعلوماتك. من شأنه أن يجبر GC لإلغاء تحديد البيانات الخاصة بك، مما تسبب في البيانات التي سيتم تنظيفها في الواقع إلا بعد الانتهاء من ذلك معها، في دورة GC الثانية. وهذا ممكن - ولها بالضبط ما وضع اللمسات الأخيرة () لل. منذ ذكرتم أن كنت لا تريد الدورة الثانية ليحدث، لا يمكن أن تعقد المعلومات الخاصة بك (إذا كان -> ب ثم ب -!> أ). وهو ما يعني يجب السماح به.

وEdit2:
في الواقع، فإن الدورة الثانية تحدث - ولكن لبك "البيانات الرئيسية"، وليس بك "البيانات سخام كبير". سيتم مسح البيانات الفعلية في الدورة الأولى.

وEdit3:
ومن الواضح أن الحل الحقيقي استخدام موضوع مستقل لإزالة من قائمة انتظار إشارة (لا الاستقصاء ()، وإزالة ()، ومنع على موضوع مخصص).

@بول - شكرا جزيلا على الإجابة و التوضيح.

@ران - أعتقد في القانون الحالي الخاص بك i++ مفقود في نهاية الحلقة.أيضا, أنت لا تحتاج إلى القيام rq.إزالة() في حلقة كما rq.استطلاع() بالفعل يزيل المرجع الأعلى ، أليس كذلك ؟

بعض النقاط:

1) كان لي أن أضيف الموضوع.النوم(1) بيان بعد++ في حلقة (لكل الحلول من بول ران) لتجنب OOM ولكن هذا لا علاقة له إلى الصورة الكبيرة و هو أيضا منصة تعتمد.الجهاز الخاص بي رباعية النواة وحدة المعالجة المركزية و هو يعمل الشمس لينكس 1.6.0_16 JDK.

2) بعد النظر في هذه الحلول سوف ابقى باستخدام finalizers.بلوخ الكتاب يقدم الأسباب التالية:

  • ليس هناك ما يضمن finalizers سيتم تنفيذها فورا ، وبالتالي لم تفعل أي شيء في الوقت الحرج في finalizer -- ولا توجد أي ضمانات SoftRererences!
  • لا تعتمد على finalizer تحديث الحرجة استمرار الدولة -- أنا لا
  • وهناك شديدة الأداء عقوبة باستخدام finalizers -- في أسوأ الأحوال سأكون لوكس عن كائن واحد في دقيقة أو نحو ذلك.أعتقد أنني يمكن أن أعيش مع ذلك.
  • استخدام try/أخيرا-نعم بالتأكيد!

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

3) Saddly, لا توجد وسيلة نقاط الانقسام بين بول, توم و ركض :( أتمنى أن توم لا تمانع كما حصل بالفعل الكثير منهم :) الحكم بين بول وركض كان أصعب بكثير - أعتقد أن كل الإجابات العمل الصحيح.أنا فقط تحديد قبول العلم أن إجابة بولس لأنه كان تصنيف أعلى (و قد شرح أكثر تفصيلا) ، ولكن ران الحل ليس سيئا على الإطلاق وربما يكون خياري إذا كنت قد اخترت أن تنفيذ ذلك باستخدام SoftReferences.شكرا لكم أيها الرجال!

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