سؤال

كنت أقرأ كتابًا عن مهارات البرمجة حيث يسأل المؤلف الشخص الذي تمت مقابلته ، "كيف يمكنك تعطل JVM؟" اعتقدت أنه يمكنك القيام بذلك عن طريق كتابة حلقة لا حصر لها من أجل استخدام كل الذاكرة في النهاية.

هل لدى أي شخص أي فكرة؟

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

المحلول

أقرب شيء إلى "إجابة" واحدة هو System.exit() الذي ينهي JVM على الفور دون التنظيف المناسب.ولكن بصرف النظر عن ذلك، فإن التعليمات البرمجية الأصلية واستنفاد الموارد هي الإجابات الأكثر ترجيحًا.وبدلاً من ذلك، يمكنك البحث في أداة تعقب الأخطاء الخاصة بشركة Sun بحثًا عن الأخطاء الموجودة في إصدار JVM الخاص بك، والتي يسمح بعضها بسيناريوهات الأعطال المتكررة.اعتدنا أن نتعرض لأعطال شبه منتظمة عند الاقتراب من حد الذاكرة البالغ 4 جيجا بايت ضمن إصدارات 32 بت (نستخدم بشكل عام 64 بت الآن).

نصائح أخرى

لن أعتبر رمي OutOfMemoryError أو StackOverflowError بمثابة عطل.هذه مجرد استثناءات عادية.لتعطل جهاز VM حقًا، هناك ثلاث طرق:

  1. استخدم JNI وتعطل في الكود الأصلي.
  2. إذا لم يتم تثبيت أي مدير أمان، فيمكنك استخدام الانعكاس لتعطل الجهاز الافتراضي.هذا خاص بالجهاز الافتراضي، ولكن عادةً ما يقوم الجهاز الافتراضي بتخزين مجموعة من المؤشرات للموارد الأصلية في الحقول الخاصة (على سبيل المثال.يتم تخزين مؤشر لكائن مؤشر الترابط الأصلي في حقل طويل في java.lang.Thread).ما عليك سوى تغييرها عبر الانعكاس وسوف يتعطل جهاز VM عاجلاً أم آجلاً.
  3. تحتوي جميع الأجهزة الافتراضية على أخطاء، لذا عليك فقط تشغيل واحدة.

بالنسبة للطريقة الأخيرة، لدي مثال قصير، والذي سيؤدي إلى تعطل جهاز Sun Hotspot VM بشكل جيد:

public class Crash {
    public static void main(String[] args) {
        Object[] o = null;

        while (true) {
            o = new Object[] {o};
        }
    }
}

يؤدي هذا إلى تجاوز سعة المكدس في GC، لذا لن تحصل على StackOverflowError، بل ستحصل على عطل حقيقي يتضمن ملف hs_err*.

JNI.في الواقع، مع JNI، التعطل هو الوضع الافتراضي للتشغيل.عليك أن تعمل بجهد إضافي حتى لا تتعطل.

استخدم هذا:

import sun.misc.Unsafe;

public class Crash {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    public static void crash() {
        unsafe.putAddress(0, 0);
    }
    public static void main(String[] args) {
        crash();
    }
}

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

جافا -Xbootclasspath/p:.يتحطم

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

ويسأل على وجه التحديد:

كيف يمكنك كتابة برنامج بلغة Java خالصة قد يتسبب في تعطل Java Virtual Machine؟

لقد قمت بالبرمجة بلغة Java لأكثر من 15 عامًا، ووجدت أن هذا السؤال محير وغير عادل.كما أشار آخرون، تم تصميم Java، باعتبارها لغة مُدارة، خصيصًا لا تحطم.بالطبع هناك دائمًا أخطاء في JVM، ولكن:

  1. بعد أكثر من 15 عامًا من استخدام JREs على مستوى الإنتاج، أصبح هذا الأمر نادرًا.
  2. من المحتمل أن يتم تصحيح أي أخطاء من هذا القبيل في الإصدار التالي، لذا ما مدى احتمالية مواجهتك كمبرمج وتذكر تفاصيل المجموعة الحالية من أدوات عرض JRE؟

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

هناك خيار آخر يتمثل في تغذية رموز البايت الزائفة لـ JRE؛من السهل تفريغ بعض البيانات الثنائية المهملة في ملف ‎.class، ومطالبة JRE بتشغيله:

$ echo 'crap crap crap' > crap.class
$ java crap
Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1668440432 in class file crap

هل هذا يحتسب؟أعني أن JRE نفسه لم يتعطل؛لقد اكتشف الرمز الزائف بشكل صحيح، وأبلغ عنه، ثم خرج.

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

إذن ماذا بقي؟أود حقًا أن أسمع ما كان يفكر فيه المؤلف حقًا كحل مناسب.

تحديث:تشاد فاولر استجاب هنا.

ملاحظة:إنه كتاب رائع.لقد التقطتها للحصول على الدعم المعنوي أثناء تعلم روبي.

سيؤدي هذا الرمز إلى تعطل JVM بطرق سيئة

import sun.dc.pr.PathDasher; 

public class Crash
{
     public static void main(String[] args)
     {    
        PathDasher dasher = new PathDasher(null) ;
     }
}

آخر مرة حاولت فيها هذا سيفعل ذلك:

public class Recur {
    public static void main(String[] argv) {
        try {
            recur();
        }
        catch (Error e) {
            System.out.println(e.toString());
        }
        System.out.println("Ended normally");
    }
    static void recur() {
        Object[] o = null;
        try {
            while(true) {
                Object[] newO = new Object[1];
                newO[0] = o;
                o = newO;
            }
        }
        finally {
            recur();
        }
    }
}

الجزء الأول من ملف السجل الذي تم إنشاؤه:

#
# An unexpected error has been detected by Java Runtime Environment:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x000000006dad5c3d, pid=6752, tid=1996
#
# Java VM: Java HotSpot(TM) 64-Bit Server VM (11.2-b01 mixed mode windows-amd64)
# Problematic frame:
# V  [jvm.dll+0x2e5c3d]
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
#

---------------  T H R E A D  ---------------

Current thread (0x00000000014c6000):  VMThread [stack: 0x0000000049810000,0x0000000049910000] [id=1996]

siginfo: ExceptionCode=0xc00000fd, ExceptionInformation=0x0000000000000001 0x0000000049813fe8 

Registers:
EAX=0x000000006dc83090, EBX=0x000000003680f400, ECX=0x0000000005d40ce8, EDX=0x000000003680f400
ESP=0x0000000049813ff0, EBP=0x00000000013f2df0, ESI=0x00000000013f0e40, EDI=0x000000003680f400
EIP=0x000000006dad5c3d, EFLAGS=0x0000000000010206

لن يتعطل تطبيق JVM المثالي أبدًا.

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

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

إذا كنت تريد تعطل JVM - استخدم ما يلي في Sun JDK 1.6_23 أو أقل:

Double.parseDouble("2.2250738585072012e-308");

ويرجع ذلك إلى أ حشرة في Sun JDK - يوجد أيضًا في OpenJDK.تم إصلاح هذا من Oracle JDK 1.6_24 وما بعده.

يعتمد على ما تقصده بالحادث.

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

يمكنك أيضًا استخدام JNI للاتصال بالرمز الأصلي.إذا لم تفعل ذلك بشكل صحيح، فيمكنك أن تجعل الأمر ينهار بشدة.يعد تصحيح أخطاء تلك الأعطال أمرًا "ممتعًا" (ثق بي، كان علي أن أكتب ملف DLL كبير لـ C++ نسميه من برنامج Java الصغير الموقع).:)

الكتاب آلة جافا الافتراضية بواسطة Jon Meyer لديه مثال لسلسلة من تعليمات الرمز الثانوي التي تسببت في تفريغ JVM الأساسي.لا أستطيع العثور على نسختي من هذا الكتاب.إذا كان لدى أي شخص هناك واحد، يرجى البحث عنه ونشر الإجابة.

على winxpsp2 ث/wmp10 jre6.0_7

Desktop.open(uriToAviOrMpgFile)

يؤدي هذا إلى قيام الخيط الناتج برمي نقطة اتصال لم يتم اكتشافها وتعطلها

YMMV

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

أقصر الطرق الممكنة :)

public class Crash
{
    public static void main(String[] args)
    {
        main(args);
    }
}

ليس تعطلًا، ولكنه أقرب إلى الانهيار من الإجابة المقبولة للاستخدام System.exit

يمكنك إيقاف JVM عن طريق الاتصال

Runtime.getRuntime().halt( status )

حسب المستندات :-

"لا تتسبب هذه الطريقة في بدء تشغيل خطافات إيقاف التشغيل ولا تقوم بتشغيل أدوات الإنهاء غير المستحثة إذا تم تمكين الإنهاء عند الخروج".

فيما يلي شرح مفصل حول أسباب قيام JVM بالتفريغ الأساسي (أي.يتحطم):http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_17534

إذا قمت بتعريف العطل على أنه إحباط عملية بسبب موقف لم تتم معالجته (أيلا يوجد استثناء أو خطأ في Java)، فلا يمكن القيام بذلك من داخل Java (إلا إذا كان لديك إذن باستخدام فئة sun.misc.Unsafe).هذا بيت القصيد من التعليمات البرمجية المُدارة.

تحدث الأعطال النموذجية في التعليمات البرمجية الأصلية عن طريق إلغاء مرجعية المؤشرات إلى مناطق خاطئة في الذاكرة (عنوان فارغ أو محاذاة بشكل خاطئ).قد يكون المصدر الآخر هو تعليمات الآلة غير القانونية (رموز التشغيل) أو الإشارات غير المعالجة من المكتبة أو مكالمات kernel.يمكن تشغيل كلاهما إذا كانت JVM أو مكتبات النظام بها أخطاء.

على سبيل المثال، يمكن أن تواجه التعليمات البرمجية (التي تم إنشاؤها) JITed أو الأساليب الأصلية أو استدعاءات النظام (برنامج تشغيل الرسومات) مشكلات تؤدي إلى أعطال حقيقية (كان من الشائع جدًا حدوث أعطال عند استخدام وظائف ZIP ونفاد الذاكرة).في تلك الحالات، يبدأ معالج الأعطال في JVM ويتخلص من الحالة.يمكنه أيضًا إنشاء ملف أساسي لنظام التشغيل (Dr.Watson على نظام التشغيل Windows والتفريغ الأساسي على *nix).

على Linux/Unix، يمكنك بسهولة تعطل JVM عن طريق إرسال إشارة إلى العملية الجارية.ملحوظة:يجب أن لا تستخدم SIGSEGV لهذا السبب، نظرًا لأن Hotspot تلتقط هذه الإشارة وتعيد طرحها باعتبارها NullPointerException في معظم الأماكن.لذلك فمن الأفضل أن ترسل أ SIGBUS على سبيل المثال.

إذا كنت تريد التظاهر بأن الذاكرة قد نفدت، فيمكنك القيام بذلك

public static void main(String[] args) {
    throw new OutOfmemoryError();
}

أعرف طريقتين لتسبب تفريغ JVM لملف خطأ عن طريق استدعاء الأساليب الأصلية (تلك المضمنة)، ولكن من الأفضل ألا تعرف كيفية القيام بذلك.;)

JNI هو مصدر كبير للحوادث.يمكنك أيضًا التعطل باستخدام واجهة JVMTI نظرًا لأنه يجب كتابتها بلغة C/C++ أيضًا.

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

public class Crash {
    public static void main(String[] args) {

        Runnable[] arr = new Runnable[1];
        arr[0] = () -> {

            while (true) {
                new Thread(arr[0]).start();
            }
        };

        arr[0].run();
    }
}

هذا أعطاني الإخراج (بعد 5 دقائق، شاهد ذاكرة الوصول العشوائي الخاصة بك)

An unrecoverable stack overflow has occurred.
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x0000000070e53ed7, pid=12840, tid=0x0000000000101078
#
# JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# 

الأقصر؟استخدم فئة Robot لتشغيل CTRL+break.لقد لاحظت ذلك عندما كنت أحاول إغلاق برنامجي دون إغلاق وحدة التحكم (لم يكن به وظيفة "الخروج").

أفعل ذلك الآن، لكن لست متأكدًا تمامًا من كيفية القيام بذلك...:-) أحيانًا تختفي JVM (وتطبيقي) تمامًا.لم يتم إلقاء أي أخطاء، ولم يتم تسجيل أي شيء.ينتقل من العمل إلى عدم التشغيل على الإطلاق على الفور دون سابق إنذار.

هل هذا يحسب ؟

long pid = ProcessHandle.current().pid();
try { Runtime.getRuntime().exec("kill -9 "+pid); } catch (Exception e) {}

إنه يعمل فقط مع Linux ومن Java 9.

لسبب ما لا أفهم ProcessHandle.current().destroyForcibly(); لا يقتل JVM ويرمي java.lang.IllegalStateException مع الرسالة تدمير العملية الحالية غير مسموح به.

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

public static void main(String[] args) {
    causeStackOverflow();
}

public void causeStackOverflow() {
    causeStackOverflow();
}

إذا كان "التعطل" هو أي شيء يقاطع jvm/البرنامج من الإنهاء العادي، فيمكن للاستثناء غير المعالج القيام بذلك.

public static void main(String args[]){
   int i = 1/0;
   System.out.print(i); // This part will not be executed due to above  unhandled exception
  }

إذن الأمر يعتمد على نوع الـCRASH؟!

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