[java] 예외가 발생하지 않더라도 try-catch 블록을 사용하는 것이 비용이 많이 듭니까?
우리는 예외를 잡는 것이 비싸다는 것을 알고 있습니다. 그러나 예외가 발생하지 않더라도 Java에서 try-catch 블록을 사용하는 것도 비용이 많이 듭니까?
스택 오버플로 질문 / 응답을 찾았습니다. try 블록은 왜 비쌉니까? 그러나 .NET 용 입니다.
답변
try
거의 비용이 들지 않습니다. try
런타임시 설정 작업을 수행하는 대신 , 코드의 메타 데이터는 컴파일 타임에 구성되어 예외가 발생했을 때 스택을 올라가고이를 try
포착 할 수 있는 블록이 있는지 비교적 비싼 작업을 수행 합니다. 예외. 평신도의 관점에서 볼 try
때 무료 일 수도 있습니다. 실제로 비용이 드는 예외를 던지고 있습니다. 그러나 수백 또는 수천 개의 예외를 던지지 않는 한 여전히 비용을 눈치 채지 못할 것입니다.
try
관련 비용이 약간 있습니다. Java try
는 그렇지 않은 블록의 코드에서 일부 최적화를 수행 할 수 없습니다 . 예를 들어, Java는 더 빠른 실행을 위해 메소드에서 명령을 재배 열하는 경우가 종종 있습니다. 그러나 Java는 예외가 발생하면 메소드의 실행이 소스 코드에서 작성된 것처럼 해당 명령문이 실행 된 것으로 간주됨을 보장해야합니다. 몇 줄까지.
try
블록에서 예외가 발생할 수 있기 때문에 (try 블록의 모든 라인에서! stop
쓰레드 를 호출 하는 등의 일부 예외는 (더 이상 사용되지 않음) OutOfMemoryError는 거의 모든 곳에서 발생할 수 있음) 비동기 적으로 발생 합니다. 같은 방법으로 코드가 계속해서 실행되고 나면, 최적화가 가능한지에 대해 추론하기가 어려워서 발생 가능성이 줄어 듭니다. (누군가 컴파일러를 프로그래밍하고, 정확성을 추론하고 보장하는 등의 작업을해야합니다. ‘예외적’이라는 것은 큰 고통이 될 것입니다.) 그러나 실제로는 이와 같은 것을 눈치 채지 못할 것입니다.
답변
측정 해 봅시다.
public abstract class Benchmark {
final String name;
public Benchmark(String name) {
this.name = name;
}
abstract int run(int iterations) throws Throwable;
private BigDecimal time() {
try {
int nextI = 1;
int i;
long duration;
do {
i = nextI;
long start = System.nanoTime();
run(i);
duration = System.nanoTime() - start;
nextI = (i << 1) | 1;
} while (duration < 100000000 && nextI > 0);
return new BigDecimal((duration) * 1000 / i).movePointLeft(3);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return name + "\t" + time() + " ns";
}
public static void main(String[] args) throws Exception {
Benchmark[] benchmarks = {
new Benchmark("try") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
try {
x += i;
} catch (Exception e) {
e.printStackTrace();
}
}
return x;
}
}, new Benchmark("no try") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += i;
}
return x;
}
}
};
for (Benchmark bm : benchmarks) {
System.out.println(bm);
}
}
}
내 컴퓨터에서 다음과 같이 인쇄됩니다.
try 0.598 ns
no try 0.601 ns
최소한이 간단한 예에서 try 문은 성능에 측정 가능한 영향을 미치지 않았습니다. 더 복잡한 것을 자유롭게 측정하십시오.
일반적으로 코드에 실제 성능 문제가 있다는 증거가 나타날 때까지 언어 구문의 성능 비용에 대해 걱정하지 않는 것이 좋습니다. 또는 도널드 크 누스 (Donald Knuth) 는 “조기 최적화는 모든 악의 근원”이라고 말합니다.
답변
try
/ catch
성능에 영향을 줄 수 있습니다. JVM이 일부 최적화를 수행하지 못하게하기 때문입니다. “유효한 Java”의 Joshua Bloch는 다음과 같이 말했습니다.
• try-catch 블록 안에 코드를 배치하면 최신 JVM 구현이 수행 할 수있는 특정 최적화가 금지됩니다.
답변
그러나 다른 사람들이 말했듯이 try
블록은 {}
주변 의 캐릭터 에 대한 일부 최적화를 금지 합니다. 특히, 옵티마이 저는 블록 내의 어느 시점에서나 예외가 발생할 수 있다고 가정해야하므로 명령문이 실행된다는 보장이 없습니다.
예를 들면 다음과 같습니다.
try {
int x = a + b * c * d;
other stuff;
}
catch (something) {
....
}
int y = a + b * c * d;
use y somehow;
을 지정하지 않으면 try
에 할당하도록 계산 된 값을 x
“공통 하위 표현식”으로 저장하고에 다시 할당 할 수 y
있습니다. 그러나 try
첫 번째 표현이 평가되었다는 보장이 없기 때문에 표현을 다시 계산해야합니다. 이것은 일반적으로 “직선”코드에서 큰 문제는 아니지만 루프에서 중요 할 수 있습니다.
그러나 이는 JITC 코드에만 적용됩니다. javac는 적은 양의 최적화 만 수행하며, 바이트 코드 인터프리터는 try
블록 을 입력 / 탈출하는 비용이 없습니다 . (블록 경계를 표시하기 위해 생성 된 바이트 코드가 없습니다.)
그리고 최선을 위해 :
public class TryFinally {
public static void main(String[] argv) throws Throwable {
try {
throw new Throwable();
}
finally {
System.out.println("Finally!");
}
}
}
산출:
C:\JavaTools>java TryFinally
Finally!
Exception in thread "main" java.lang.Throwable
at TryFinally.main(TryFinally.java:4)
javap 출력 :
C:\JavaTools>javap -c TryFinally.class
Compiled from "TryFinally.java"
public class TryFinally {
public TryFinally();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]) throws java.lang.Throwable;
Code:
0: new #2 // class java/lang/Throwable
3: dup
4: invokespecial #3 // Method java/lang/Throwable."<init>":()V
7: athrow
8: astore_1
9: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
12: ldc #5 // String Finally!
14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
17: aload_1
18: athrow
Exception table:
from to target type
0 9 8 any
}
“GOTO”가 없습니다.
답변
최적화를 수행 할 수없는 이유를 이해하려면 기본 메커니즘을 이해하는 것이 좋습니다. 내가 찾을 수있는 가장 간결한 예제는 C 매크로에서 구현되었습니다 : http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
#include <stdio.h>
#include <setjmp.h>
#define TRY do{ jmp_buf ex_buf__; switch( setjmp(ex_buf__) ){ case 0: while(1){
#define CATCH(x) break; case x:
#define FINALLY break; } default:
#define ETRY } }while(0)
#define THROW(x) longjmp(ex_buf__, x)
컴파일러는 점프가 X, Y 및 Z로 지역화 될 수 있는지 여부를 결정하는 데 어려움을 겪기 때문에 안전하다고 보장 할 수없는 최적화를 건너 뛰지 만 구현 자체는 다소 가볍습니다.
답변
또 다른 마이크로 벤치 마크 ( source ).
예외 백분율에 따라 try-catch 및 no-try-catch 코드 버전을 측정하는 테스트를 만들었습니다. 10 % 백분율은 테스트 사례의 10 %가 0 건으로 나눈 것을 의미합니다. 어떤 상황에서는 try-catch 블록에 의해 처리되고 다른 상황에서는 조건부 연산자에 의해 처리됩니다. 내 결과 테이블은 다음과 같습니다.
OS: Windows 8 6.2 x64
JVM: Oracle Corporation Java HotSpot(TM) 64-Bit Server VM 23.25-b01
백분율 | 결과 (시도 / if, ns) 0 % | 88/90 1 % | 89/87 10 % | 86/97 90 % | 85/83
어떤 경우에도 큰 차이가 없다고 말합니다.
답변
NullPointException을 잡는 것이 꽤 비싸다는 것을 알았습니다. 1.2k 작업의 경우 시간이 200ms와 12ms로 동일한 방식으로 처리했을 때 나 if(object==null)
에게 꽤 개선되었습니다.