java - شرح - تحميل جافا 64 بت ويندوز 10




لماذا من أي وقت مضى تنفيذ الصيغة النهائية()؟ (14)

finalize () هو تلميح إلى JVM أنه قد يكون من الجيد تنفيذ التعليمات البرمجية الخاصة بك في وقت غير محدد. هذا جيد عندما تريد رمز فشل في تشغيل غامضة.

كما أن القيام بأي شيء مهم في الرسائل النهائية (أي شيء باستثناء التسجيل) جيد أيضًا في ثلاث حالات:

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

إذا كنت تعتقد أنك بحاجة إلى وضع اللمسات الأخيرة () ، فأحيانًا ما تريده فعلاً هو إشارة وهمية (والتي يمكن أن تحتوي في المثال المعطى على مرجع صلب إلى اتصال يستخدمه الرجوع ، وإغلاقه بعد وضع إشارة الوهمية في قائمة الانتظار). هذا أيضا لديه خاصية أنه قد لا يتم تشغيله بشكل غامض ، ولكن على الأقل لا يمكنه استدعاء طرق أو إعادة إحياء الكائنات النهائية. لذا فمن المناسب فقط المواقف التي لا تحتاج فيها إلى إغلاق هذا الاتصال تمامًا ، ولكنك ترغب في ذلك تمامًا ، ولا يستطيع عملاء صفك الاتصال أو لا يمكنهم الاتصال بأنفسهم (وهو أمر عادل بما فيه الكفاية - ما هو الهدف من وجود جامع قمامة على الإطلاق إذا كنت تقوم بتصميم واجهات تتطلب اتخاذ إجراء محدد قبل الجمع؟ وهذا يعيدنا إلى الوراء في أيام malloc / free.)

في أحيان أخرى ، تحتاج إلى المورد الذي تعتقد أنه قادر على أن يكون أكثر قوة. على سبيل المثال ، لماذا تحتاج إلى إغلاق هذا الاتصال؟ يجب أن يعتمد في النهاية على نوع من I / O يوفره النظام (مقبس ، ملف ، أيا كان) ، فلماذا لا يمكنك الاعتماد على النظام لإغلاقه لك عندما يتم اقتطاف أدنى مستوى من الموارد؟ إذا كان الخادم في الطرف الآخر يتطلب منك تمامًا إغلاق الاتصال بشكل صحيح بدلاً من إسقاط المقبس ، فما الذي سيحدث عند قيام شخص ما بالسير فوق كابل الطاقة الخاص بالجهاز الذي تعمل عليه الكود ، أم تنتهي الشبكة المتداخلة؟

تنويه: لقد عملت على تنفيذ JVM في الماضي. أنا أكره finalizers.

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

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

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

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


iirc - يمكنك استخدام طريقة وضع اللمسات الأخيرة كوسيلة لتنفيذ آلية التجميع لموارد باهظة الثمن - بحيث لا تحصل على GC أيضًا.


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

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

انظر إلى فصول "المرجع". مرجع ضعيف ، إلخ.

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

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

public void finalize() {
  ref1 = null;
  ref2 = null;
  othercrap = null;
}

إنها علامة على أن شخصًا ما لم يكن يعرف ما يفعله. "تنظيف" مثل هذا هو في الواقع لا حاجة إليه. عندما يكون الفصل هو GC'd ، يتم ذلك تلقائيًا.

إذا وجدت رمزًا كهذا في وضع اللمسات الأخيرة ، فيجب التأكد من أن الشخص الذي كتبه كان مرتبكًا.

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

لا ينبغي أبدا أن تستخدم لتنظيف الأشياء "أسرع".


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

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

@Override
public void finalize()
{
    try {saveCache();} catch (Exception e)  {e.printStackTrace();}
}

public void saveCache() throws FileNotFoundException, IOException
{
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp"));
    out.writeObject(cache);
}

قاعدة بسيطة: لا تستخدم أبدًا finalizers. الحقيقة وحدها أن كائن يحتوي على finalizer (بغض النظر عن التعليمات البرمجية التي ينفذها) كافية لتسبب مقدار كبير من البيانات المهملة لجمع البيانات المهملة.

من article بقلم بريان جويتز:

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


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


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

من الأفضل تجنب ذلك ، مع استثناء محتمل لتشخيصات test-env.

يحتوي Eckel Thinking في Java على قسم جيد في هذا.


لست متأكدًا مما يمكنك فعله ، لكن ...

[email protected] ~/jdk1.6.0_02/src/
$ find . -name "*.java" | xargs grep "void finalize()" | wc -l
41

لذا أعتقد أن الشمس وجدت بعض الحالات التي (يعتقدون) أنها يجب أن تستخدم.


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


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


يمكن أن يكون مفيدًا لإزالة الأشياء التي تمت إضافتها إلى مكان عالمي / ثابت (بدون الحاجة) ، ويجب إزالتها عند إزالة الكائن. على سبيل المثال:

    private void addGlobalClickListener() {
        weakAwtEventListener = new WeakAWTEventListener(this);

        Toolkit.getDefaultToolkit().addAWTEventListener(weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK);
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();

        if(weakAwtEventListener != null) {
            Toolkit.getDefaultToolkit().removeAWTEventListener(weakAwtEventListener);
        }
    }

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

لقد قمت بالبرمجة في Java منذ 1.0 alpha 3 (1995) ولم أتوقف عن وضع اللمسات النهائية لأي شيء ...


يمكنك استخدامه بمثابة backstop لكائن يحمل مورد خارجي (مقبس ، ملف ، الخ). تنفيذ close() وثيقة وثيقة أنه يحتاج إلى أن يسمى.

تنفيذ finalize() للقيام close() إذا اكتشفت أنه لم يتم إنجازه. ربما مع شيء ملقاة على stderr للإشارة إلى أنك تنظيف بعد المتصل عربات التي تجرها الدواب.

يوفر سلامة إضافية في حالة استثنائية / عربات التي تجرها الدواب. لن يقوم كل متصل بإجراء try {} finally {} الصحيحة try {} finally {} كل مرة. لسوء الحظ ، ولكن صحيح في معظم البيئات.

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

أرى أنه اعتبارا من 9 جافا ، تم إهمال Object.finalize() ! أنها تشير لنا إلى java.lang.ref.Cleaner و java.lang.ref.PhantomReference كبدائل.


class MyObject {
    Test main;

    public MyObject(Test t) {    
        main = t; 
    }

    protected void finalize() {
        main.ref = this; // let instance become reachable again
        System.out.println("This is finalize"); //test finalize run only once
    }
}

class Test {
    MyObject ref;

    public static void main(String[] args) {
        Test test = new Test();
        test.ref = new MyObject(test);
        test.ref = null; //MyObject become unreachable,finalize will be invoked
        System.gc(); 
        if (test.ref != null) System.out.println("MyObject still alive!");  
    }
}

====================================

نتيجة:

هذا هو وضع اللمسات الأخيرة

MyObject لا يزال على قيد الحياة!

=====================================

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





jvm