برنامه نویسی

آیا شما با استثنائات اشتباه رفتار می کنید؟ دریابید که هیچ کس در مورد استثنائات جاوا به شما نمی گوید! قسمت 2

فهرست

  1. مقدمه
  2. ردیابی پشته: شرور واقعی
  3. استثنائات استاتیک در مقابل دینامیک
  4. بهینه سازی: FillinstackTrace ()
  5. Undinding Stack: هزینه راه اندازی
  6. پرچم ها در مقابل استثنا
  7. مثال: اگر در مقابل استثنا
  8. مثال: استثنائات در حلقه
  9. تجزیه و تحلیل تجربی: تأثیر استثنائات بر عملکرد API

  10. پایان
  11. مرجع کتابشناسی

مقدمه

بخش اول مقاله ما به شیوه های خوب ، تله ها و مبانی مفهومی استفاده از استثنائات جاوا پرداخته است. اکنون وقت آن رسیده است که بیشتر پیش برویم و درک کنیم که چگونه آنها مستقیماً بر عملکرد برنامه شما تأثیر می گذارند. و پاسخ ممکن است شما را شگفت زده کند.

در این مقاله دوم ، ما به سؤالاتی مانند:

  • آیا استثنائات واقعاً کند هستند؟
  • هزینه واقعی ایجاد و راه اندازی یک استثناء چقدر است؟
  • چه زمانی استفاده از آنها قابل قبول است؟
  • و وقتی این یک تله ساکت می شود؟

با تست های واقعی ، معیارها ، کد مثال و منابع وزنی مانند Aleksey Shipilev ، سرانجام خواهید فهمید که چه اتفاقی در زیر پارچه می افتد.


ردیابی پشته: شرور واقعی

یک استثنا با new Exception() این فقط یک شیء نیست. در حقیقت ، این فرایند شامل ضبط اثری کامل از لحظه لحظه ای است که می تواند پرهزینه باشد.

هزینه ضبط ردیابی پشته متناسب با عمق شمع تماس است. هرچه روشهای بیشتری انباشته شود ، زمان جاوا برای ساخت نمای شمع بیشتر خواهد بود.

مثال واقعی معیار:

  • عمق 0: microseconds 2.0 ~
  • عمق 1024: 80 میکرو ثانیه

از طرف دیگر ، یک تماس ساده به یک روش بدون استثنا می تواند کمتر از 1 نانو ثانیه هزینه کند!


استثنائات استاتیک در مقابل دینامیک

استثنائات پویا ایجاد شده (با new) همیشه این هزینه ردیابی پشته را حمل کنید. استاتیک ، از قبل ایجاد شده و استثنائات استفاده مجدد ، از این هزینه خودداری کنید.

private static final MinhaException EX = new MinhaException("Erro comum");

throw EX;
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این استاندارد هزینه نمونه را از بین می برد و پرتاب می کند تقریباً به همان سرعت returnبشر


بهینه سازی: fillInStackTrace()

ترفند دیگر غلبه بر روش است fillInStackTrace():

@Override
public synchronized Throwable fillInStackTrace() {
    return this;
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این عمل ضبط ردیابی پشته را از بین می برد. نتیجه؟ استثناء بسیار سبک – بلکه همچنین پاکسازی بسیار سخت تر استبشر با پارسیمونی استفاده کنید.


Undinding Stack: هزینه راه اندازی

یک استثنا را راه اندازی کنید (با throw) روند کار را تحریک می کند از پس دادنیعنی JVM باید باتری تماس ها را طی کند تا زمانی که پیدا کند catch کافی

سعید catch در همان روش است: 230ns پوند
اگر 10 سطح بالاتر باشد: می تواند به 8000 پوند یا بیشتر برسد

در مقابل ، بازده عادی بین همین روشها 1NS هزینه دارد.


پرچم ها در مقابل استثنا

یک الگوی مشترک برای جلوگیری از استثناء استفاده از پرچم ها یا بسته بندی ها است Optionalبشر

public Optional<Usuario> buscar(String id) { ... }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  • پرچم در عملکرد ثابت هستند.
  • استثناء فقط وقتی کارآمد هستند ناچیزبشر

فرکانس پایین (<0.01 ٪) ، استثنائات قابل قبول و حتی سریعتر است. در فرکانس متوسط ​​یا بالا ، هزینه منفجر می شود.


مثال: اگر در مقابل استثنا

package org.example;

public class ExceptionVsIfSingleCall {
    public static void main(String[] args) {
        long start, end;

        // Controle com if
        start = System.nanoTime();
        processWithIf(3);
        end = System.nanoTime();
        System.out.println("Com if: 800 ns");

        // Controle com exceção
        start = System.nanoTime();
        try {
            processWithException(3);
        } catch (IllegalArgumentException e) {}
        end = System.nanoTime();
        System.out.println("Com exceção: 24100 ns");
    }

    public static int processWithIf(int value) {
        if (value < 5) return 0;
        return value;
    }

    public static void processWithException(int value) {
        if (value < 5) throw new IllegalArgumentException("valor inválido");
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ای if بیش از 30 برابر سریعتر بود.


مثال: استثنائات در حلقه

package org.example;

public class ExceptionOverheadLoopBenchmark {
    public static void main(String[] args) throws Exception {
        final int N = 10_000_000;

        long start, end;

        // Execução pura
        start = System.nanoTime();
        for (int i = 0; i < N ; i++) { int x = i * 2; }
        end = System.nanoTime();
        System.out.println("execução pura: 2.1322 ms");

        // If
        start = System.nanoTime();
        for (int i = 0; i < N ; i++) {
            if (i % 2 == 0) { int x = i * 2; }
        }
        end = System.nanoTime();
        System.out.println("if: 2.6529 ms");

        // Try-catch sem exceção
        start = System.nanoTime();
        for (int i = 0; i < N; i++) {
            try { int x = i * 2; } catch (Exception e) {}
        }
        end = System.nanoTime();
        System.out.println("try-catch (sem erro): 3.267699 ms");

        // Try-catch com exceção
        start = System.nanoTime();
        for (int i = 0; i < N / 100; i++) {
            try { throw new Exception("erro"); } catch (Exception e) {}
        }
        end = System.nanoTime();
        System.out.println("try-catch (com erro): 72.4698 ms");
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

استثنائات در حلقه ها فقط کند نیست: آنها مخرب هستند.

این مطالعه عملی یافته های مقاله “عملکرد استثنایی لیل” توسط Aleksey Shipilëv را تأیید می کند و دو اجرای API را آزمایش می کند: یکی از استثنائات برای خطاهای سیگنال و دیگری با استفاده از الگوی بسته بندی Resultبشر

استفاده کردن: کلیه کدهای منبع مورد استفاده در این مطالعه ، شواهدی از آزمایشات مربوط به اسکریپت های Jmeter و Test در مخزن https://github.com/diegosbrandao/diegosbranda-exceptions-java موجود است. نتایج آزمون را می توان در پوشه “شواهد” یافت و اسکریپت های Jmeter در دسترس کسانی است که می خواهند آزمایشات را تولید کنند.

روش شناسی

دو نسخه از همان API با رفتار عملکردی یکسان اجرا شد:

  1. نسخه استثناء: استفاده کنید RuntimeException و استثنائات سفارشی برای خطاهای سیگنال
  2. نسخه بسته بندی: از استاندارد استفاده می کند Result برای محاصره موفقیت یا شکست
public class Result<T> {
    private final boolean success;
    private final T value;
    private final String errorMessage;

    // Construtor e métodos de fábrica
    public static <T> Result<T> success(T value) { ... }
    public static <T> Result<T> error(String message) { ... }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

هر دو API با تنظیمات زیر در تست های بار در Apache Jmeter قرار گرفتند:

  • 20 موضوع (کاربران رقیب)
  • دوره گرمایش (شیب دار): 15 ثانیه
  • 5 تکرار توسط موضوع
  • مجموع: 300 درخواست (20 موضوع × 5 تکرار × 3 اعدام)

توجه: نتایج عملکرد ارائه شده مخصوص محیط سخت افزار/نرم افزار مورد استفاده در آزمایشات است. پردازنده های مختلف ، حافظه ، سیستم عامل و تنظیمات JVM می توانند در مقادیر مطلق تغییرات قابل توجهی ایجاد کنند ، اگرچه نسبت های کلی و نتیجه گیری های کلی تمایل به ادامه دارد. هنگام بازتولید این آزمایشات ، با تفسیر نتایج ، مشخصات محیط خود را در نظر بگیرید.

نتایج مقایسه ای

متریک API با استثنا API بدون استثنا تفاوت
نمونه 300 300 جدید
زمان متوسط ​​(MS) 3 1 3 × کندتر
حداقل زمان (MS) 2 1 2 × کندتر
حداکثر زمان (MS) 194 4 48.5 × کندتر
انحراف معیار 11.13 0.58 19.2 × متغیر ترین
میزان خطا 0.00 ٪ 0.00 ٪ جدید
توان 2.6/ثانیه 2.6/ثانیه جدید

تجزیه و تحلیل نتایج

1. زمان پاسخ

میانگین زمان پاسخ API با استثنائات (3ms) سه برابر بیشتر از API با استفاده از استاندارد نتیجه (1ms) است و تأیید سربار پردازش تحمیل شده توسط استثنائات.

2. پیش بینی عملکرد

انحراف API استاندارد با استثنائات (11.13) به طور قابل توجهی بالاتر از API بدون استثنائات (0.58) است ، و یک تنوع 19 × بالاتر را برجسته می کند. این ناسازگاری برنامه ریزی ظرفیت را دشوار می کند و تجربه کاربر را مختل می کند.

3. اوج و قله های تأخیر

جالب ترین تفاوت حداکثر زمان پاسخ است: 194ms برای استثنائات در مقابل تنها 4ms برای استاندارد نتیجه. این نشانگر اوج تأخیر 48.5 × بالاتر است و تأیید یکی از مهمترین نتیجه گیری های مقاله است: استثنائات می تواند باعث قله تأخیر غیرقابل قبول شدید در برنامه های حساس -زمان شود.

4. معادل توان

توان در هر دو روش یکسان (2.6 req/s) یکسان باقی مانده است ، نشان می دهد که در سطح ترافیک عادی ، انتخاب بین استثنائات و بسته بندی ها به طور مستقیم بر ظرفیت پردازش API تأثیر نمی گذارد.

همبستگی با مقاله اصلی

نتایج ما به صورت تجربی چندین نتیجه گیری در مقاله “عملکرد استثنایی استثناء لیل” را تأیید می کند:

  1. هزینه ساخت و ساز ردیابی پشته: در این مقاله آمده است که ساخت ردیابی پشته یکی از اصلی ترین فاکتورهای استثنائات است که به طور متوسط ​​3 × زمان طولانی تر ما توضیح می دهد.

  2. تنوع عملکرد: در این مقاله آمده است که عملکرد استثنائات غیرقابل پیش بینی است ، که توسط انحراف استاندارد بزرگتر 19 در API ما با استثنائات تأیید شده است.

  3. قله های تأخیر: در این مقاله آمده است که استثنائات می توانند قله های تأخیر شدید ایجاد کنند ، که در حداکثر زمان ما 48.5 × طولانی تر مشهود است.

  4. قانون فرکانس تجربی: مقاله نشان می دهد که استثنائات فقط در صورت بروز فرکانس کمتر از 10⁻⁴ (0.01 ٪) قابل قبول هستند. نتایج ما نشان می دهد که حتی با فرکانس های کم ، تأثیر بر قله های تأخیر همچنان قابل توجه است.

ملاحظات طراحی

چه زمانی از استثنائات استفاده کنید:

  • برای شرایط واقعاً استثنایی (فرکانس <0.01 ٪)
  • هنگامی که سادگی کد از عملکرد قابل پیش بینی مهمتر است
  • در سناریوهایی که قله های گاه به گاه تاخیر قابل قبول است
  • در مواقعی که نشان دهنده خطاهای واقعی در جریان اجرای است
  • برای موارد استثنایی که قطع جریان طبیعی مناسب است

استثنائات و مکانیسم امتحان در هنگام استفاده صحیح بسیار مفید هستند. آنها خوانایی کد را بهبود می بخشند ، جریان اصلی درمان خطا را از هم جدا می کنند و به شما امکان می دهند مشکلات را در سطوح بالاتر برنامه ضبط کنید. در موارد واقعاً استثنایی مانند خرابی سیستم ، خطاهای پیکربندی یا شرایط غیر منتظره ، استثنائات ابزار مناسب هستند و حتی ممکن است عملکرد مسیر اجرای عادی را بهبود بخشند.

بسته های Quando Usar (نتیجه/اختیاری):

  • برای شکست های مورد انتظار یا مکرر
  • هنگامی که پیش بینی عملکرد بسیار مهم است
  • در خدمات در دسترس بودن بالا که در آن P99 و P999 کنترل می شوند
  • در API های تأخیر کم که قله ها غیرقابل قبول هستند
  • برای عملیاتی که “خطا” یک نتیجه ممکن و مورد انتظار است

نتایج این مطالعه عملی نتیجه گیری نظری مقاله اصلی را تأیید می کند: استثنائات به طور قابل توجهی بر عملکرد و پیش بینی API تأثیر می گذارد. Result این عملکرد سازگار و قابل پیش بینی تر و بدون قله های تأخیر نشان داده است ، و آن را برای سیستم های حساس به زمان و در دسترس بودن مناسب تر می کند.

برای برنامه هایی که پیش بینی عملکرد بسیار مهم است ، داده های ما به شدت پیشنهاد می کند که الگوهای جایگزین به استثنائات ، مانند موارد Result در این مطالعه اجرا شده است.


پایان

  • استثنائات دو هزینه اصلی دارند: ردیابی پشته و پشته.
  • هرچه شمع عمیق تر باشد ، هزینه ایجاد استثنا نیز بیشتر است.
  • دورتر catch، هزینه بالاتر throwبشر
  • از استثنائات در منطق اعتبار سنجی مشترک یا حلقه ها خودداری کنید.
  • از استثنائات برای … استثنائات استفاده کنید. موارد بسیار نادر و غیر منتظره.

استثنائات به خوبی استفاده شده مسیر “شاد” را بهینه می کنند. آنها معمولاً مورد استفاده قرار می گیرند ، آنها کل برنامه خود را مجازات می کنند.


مرجع کتابشناسی

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا