Wiemy, że wychwytywanie wyjątków jest drogie. Ale czy korzystanie z bloku try-catch w Javie jest również kosztowne, nawet jeśli wyjątek nigdy nie jest zgłaszany?
Znalazłem pytanie / odpowiedź Przepełnienie stosu Dlaczego blokowanie prób jest drogie? , ale dotyczy .NET .
java
performance
try-catch
Jsedano
źródło
źródło
try { /* do stuff */ } finally { /* make sure to release resources */ }
jest legalny i użytecznyfinally
bloku używająctry-with-resources
Odpowiedzi:
try
nie ma prawie żadnych kosztów. Zamiast wykonywać konfiguracjętry
w czasie wykonywania, metadane kodu są ustrukturyzowane w czasie kompilacji, tak że kiedy zgłoszony jest wyjątek, wykonuje on stosunkowo kosztowną operację wchodzenia na stos i sprawdzania, czytry
istnieją jakieś bloki, które mogłyby to wychwycić wyjątek. Z punktu widzenia laikatry
równie dobrze może być bezpłatny. W rzeczywistości rzuca wyjątek, który cię kosztuje - ale dopóki nie wprowadzisz setek lub tysięcy wyjątków, nadal nie zauważysz kosztu.try
wiąże się z tym niewielkie koszty. Java nie może dokonać optymalizacji kodu wtry
bloku, który w przeciwnym razie by zrobił. Na przykład Java często zmienia kolejność instrukcji w metodzie, aby przyspieszyć jej działanie - ale Java musi także zagwarantować, że jeśli zostanie zgłoszony wyjątek, wykonanie metody jest obserwowane tak, jakby jej instrukcje zapisane w kodzie źródłowym były wykonywane w kolejności do jakiejś linii.Ponieważ w
try
bloku może zostać zgłoszony wyjątek (w dowolnej linii w bloku try! Niektóre wyjątki są zgłaszane asynchronicznie, na przykład przez wywołaniestop
wątku (który jest przestarzały), a nawet poza tym OutOfMemoryError może się zdarzyć prawie wszędzie), a jednak to może zostać przechwycony, a kod będzie kontynuowany po tej samej metodzie, trudniej jest uzasadnić optymalizacje, które można wprowadzić, więc jest mniej prawdopodobne, że tak się stanie. (Ktoś musiałby zaprogramować kompilator, aby to robił, uzasadniał i gwarantował poprawność, itp. Byłoby to wielkim problemem dla czegoś, co ma być „wyjątkowe”). Ale w praktyce nie zauważysz takich rzeczy.źródło
try...finally
blok bezcatch
uniemożliwia również pewne optymalizacje?Exception
obiektu zajmuje najwięcej czasu.Zmierzmy to, prawda?
Na moim komputerze wyświetla się coś takiego:
Przynajmniej w tym trywialnym przykładzie instrukcja try nie miała mierzalnego wpływu na wydajność. Możesz zmierzyć bardziej złożone.
Ogólnie rzecz biorąc, zalecam nie przejmować się kosztem wydajności konstrukcji językowych, dopóki w kodzie nie ma dowodów na rzeczywisty problem z wydajnością. Lub, jak ujął to Donald Knuth : „przedwczesna optymalizacja jest źródłem wszelkiego zła”.
źródło
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
, zarówno pętla, jak i jej dodatek są obecne w generowanym kodzie natywnym. I nie, metody abstrakcyjne nie są wbudowane, ponieważ ich program wywołujący nie jest kompilowany w czasie (prawdopodobnie dlatego, że nie jest wywoływany wystarczająco często).try
/catch
może mieć pewien wpływ na wydajność. Wynika to z faktu, że uniemożliwia ona JVM optymalizację. Joshua Bloch w „Skutecznej Javie” powiedział:źródło
Tak, jak powiedzieli inni,
try
blok hamuje niektóre optymalizacje{}
otaczających go postaci. W szczególności optymalizator musi założyć, że w dowolnym punkcie bloku może wystąpić wyjątek, więc nie ma pewności, że instrukcje zostaną wykonane.Na przykład:
Bez tego
try
wartość obliczona do przypisaniax
może zostać zapisana jako „wspólne podwyrażenie” i ponownie użyta do przypisaniay
. Ale z powodutry
braku pewności, że pierwsze wyrażenie zostało kiedykolwiek ocenione, więc wyrażenie musi zostać ponownie obliczone. Zwykle nie jest to wielka sprawa w kodzie „liniowym”, ale może być znacząca w pętli.Należy jednak zauważyć, że dotyczy to TYLKO kodu JITCed. javac robi tylko optymistyczną optymalizację, a interpretator kodu bajtowego ma zerowy koszt wejścia / wyjścia z
try
bloku. (Nie ma generowanych kodów bajtowych do oznaczenia granic bloków).A dla bestsss:
Wynik:
wyjście javap:
Brak „GOTO”.
źródło
catch/finally
ramki.finally
w kodzie bajtowym, to jesttry/catch(Throwable any){...; throw any;}
i ma instrukcję catch z ramką i Throwable, które MUSZĄ BYĆ zdefiniowane (non null) i tak dalej. Dlaczego próbujesz spierać się na ten temat, możesz sprawdzić przynajmniej jakiś kod bajtowy? Obecne wytyczne dotyczące impl. w końcu kopiuje bloki i omija sekcję goto (poprzedni impl), ale kody bajtowe należy skopiować w zależności od liczby punktów wyjścia.Aby zrozumieć, dlaczego nie można przeprowadzić optymalizacji, warto zrozumieć podstawowe mechanizmy. Najbardziej zwięzły przykład, jaki udało mi się znaleźć, został zaimplementowany w makrach C pod adresem : http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
Kompilatory często mają trudności z określeniem, czy skok można zlokalizować na X, Y i Z, więc pomijają optymalizacje, których nie mogą zagwarantować bezpieczeństwa, ale sama implementacja jest raczej lekka.
źródło
Kolejny mikrobenchmark ( źródło ).
Stworzyłem test, w którym mierzę wersję kodu try-catch i no-try-catch na podstawie wartości procentowej wyjątku. 10% procent oznacza, że 10% przypadków testowych miało podział na zero przypadków. W jednej sytuacji jest obsługiwany przez blok try-catch, w drugiej przez operator warunkowy. Oto moja tabela wyników:
Co oznacza, że nie ma znaczącej różnicy między tymi przypadkami.
źródło
Odkryłem, że łapanie NullPointException jest dość drogie. W przypadku operacji 1,2 tys. Czas wynosił 200 ms i 12 ms, kiedy wymieniałem go w ten sam sposób,
if(object==null)
co było dla mnie dość poprawą.źródło