예외조항 중 어떤 부분이 비용이 많이 드나요?
Java에서 실제로 오류가 없을 때 throw/catch를 로직의 일부로 사용하는 것은 일반적으로 좋지 않습니다. 왜냐하면 예외를 던지고 잡는 것은 비용이 많이 들고 루프에서 여러 번 실행하는 것은 일반적으로 예외를 던지지 않는 다른 제어 구조보다 훨씬 느리기 때문입니다.
질문입니다만, 비용은 슬로우/캐치 자체에 발생합니까, 아니면 Exception 객체를 작성할 때 발생합니까(실행 스택을 포함한 많은 런타임 정보를 얻을 수 있기 때문에).
바꿔 말하면, 만약 제가
Exception e = new Exception();
던지지 마세요. 던지기 비용의 대부분은 던지기 비용입니까? 아니면 던지기 + 캐치가 비용을 감당하고 있습니까?
코드를 try/catch block에 넣는 것이 그 코드를 실행하는 데 비용을 더하는 것이 아니라 예외를 잡는 것이 비용이 많이 드는 부분인지, 아니면 예외 생성(컨스트럭터 호출)을 하는 것이 비용이 많이 드는 부분인지 물어보는 것입니다.
또 다른 질문 방법은 예외 인스턴스를 하나 만들고 여러 번 던지고 잡으면 매번 새로운 예외를 만드는 것보다 훨씬 빠를까요?
예외 개체를 만드는 데 다른 일반 개체를 만드는 것보다 비용이 많이 드는 것은 아닙니다.주요 비용은 콜 스택을 통과하여 스택트레이스를 구축하기 위해 필요한 모든 정보(클래스, 메서드명, 회선번호 등)를 수집하는 네이티브 방식에는 숨겨져 있습니다.
.Throwable
으로 '어디로 가느냐'라고 부릅니다.fillInStackTrace
여기서 예외 작성 속도가 느리다는 생각이 나옵니다.단, 1개의 컨스트럭터가 있습니다.Throwable
스택 트레이스 없음그것은 당신이 매우 빠르게 인스턴스화 할 수 있는 던질 수 있는 것을 만들 수 있게 해준다.또 다른 방법은 Lightweight를 덮어쓰는 것입니다.fillInStackTrace
.
이제 예외를 두는 건 어때?
실제로, 이는 발생한 예외가 어디에서 포착되는지에 따라 달라집니다.
검출된 경우(같은의 메서드가 될 수 인라이닝으로 된 경우.throw
간단하다.goto
(「JIT」).
단, 「」, 「」의 경우catch
JVM이 스택 프레임을 풀어야 하므로 시간이 훨씬 더 오래 걸릴 수 있습니다.더 요.synchronized
언와인딩은 삭제된 스택프레임에서 소유하는 모니터를 해방하는 것을 의미하기 때문에 관련된 블록 또는 메서드입니다.
적절한 벤치마크를 통해 위의 내용을 확인할 수 있었지만 다행히 HotSpot의 퍼포먼스 엔지니어 Alexey Shipilev의 게시물에서 모든 측면이 완벽하게 다루어져 있기 때문에 이 작업은 수행할 필요가 없습니다.Lil' Exception의 뛰어난 퍼포먼스.
의 첫 Throwable
컨스트럭터는 스택트레이스를 채우는 것으로 대부분의 비용이 발생합니다.
단, 스택트레이스를 디세블로 하는 플래그를 가진 보호된 컨스트럭터가 있습니다.확장 시 이 생성자에 액세스할 수 있습니다.Exception
예외 하고 보다 적은시킬 수 .커스텀 예외 타입을 작성하면 스택트레이스 작성을 회피할 수 있어 보다 적은 정보로 퍼포먼스를 향상시킬 수 있습니다.
통상적인 방법으로 임의의 유형의 예외를 1개 작성하면 스택트레이스를 채우는 오버헤드 없이 여러 번 재투입할 수 있습니다.단, 스택트레이스에는 특정 인스턴스의 어디에 배치되었는지가 아니라 작성된 위치가 반영됩니다.
현재 버전의 Java에서는 스택트레이스 작성을 최적화하려고 합니다.스택 트레이스를 채우기 위해 네이티브코드가 호출되며 트레이스는 보다 가벼운 네이티브 구조로 기록됩니다.대응하는 Java 오브젝트는 이 레코드에서 느릿느릿 작성됩니다.getStackTrace()
,printStackTrace()
또는 트레이스를 필요로 하는 다른 메서드가 호출됩니다.
스택 트레이스 생성을 배제하는 경우 다른 주요 비용은 슬로우와 캐치 사이의 스택을 분리하는 것입니다.예외가 검출되기 전에 발생하는 간섭 프레임의 수가 적을수록 이 속도는 빨라집니다.
정말 예외적인 경우에만 예외가 발생하도록 프로그램을 설계하고 이러한 최적화는 정당화하기 어렵습니다.
여기 예외에 대한 좋은 설명이 있습니다.
http://shipilev.net/blog/2014/exceptional-performance/
결론적으로 스택 트레이스 구축과 스택 언바인딩은 비용이 많이 드는 부분입니다.다음 코드는 다음 기능의 이점을 활용합니다.1.7
을 사용하다 이것을 어떤 이 발생하는지 할 수 있습니다.
오브젝트 작성만을 위한 타이밍은 다음과 같습니다.추가했습니다.String
을 작성해도을 알 수 .JavaException
및 '''a''String
가 켜져 한 . 스택 쓰기가 켜져 있으면 적어도 1배는 느려집니다.
Time to create million String objects: 41.41 (ms)
Time to create million JavaException objects with stack: 608.89 (ms)
Time to create million JavaException objects without stack: 43.50 (ms)
다음은 특정 깊이에서 100만 번 던지고 돌아오는 데 걸린 시간을 보여줍니다.
|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%)|
| 16| 1428| 243| 588 (%)|
| 15| 1763| 393| 449 (%)|
| 14| 1746| 390| 448 (%)|
| 13| 1703| 384| 443 (%)|
| 12| 1697| 391| 434 (%)|
| 11| 1707| 410| 416 (%)|
| 10| 1226| 197| 622 (%)|
| 9| 1242| 206| 603 (%)|
| 8| 1251| 207| 604 (%)|
| 7| 1213| 208| 583 (%)|
| 6| 1164| 206| 565 (%)|
| 5| 1134| 205| 553 (%)|
| 4| 1106| 203| 545 (%)|
| 3| 1043| 192| 543 (%)|
다음은 거의 확실히 지나치게 단순화된 것이다.
스택 쓰기를 16으로 하면 오브젝트 작성에 약 40%의 시간이 소요됩니다.실제 스택트레이스는 이 대부분을 차지합니다.JavaException 객체의 인스턴스화의 최대 93%는 스택트레이스가 취득되었기 때문입니다.즉, 이 경우 스택을 풀려면 나머지 50%의 시간이 걸립니다.
스택 트레이스 오브젝트의 작성은 20 %로, 스택 언바인딩은 시간의 80%를 차지합니다.
어느 경우든 스택 언와인딩은 전체 시간의 대부분을 차지합니다.
public class JavaException extends Exception {
JavaException(String reason, int mode) {
super(reason, null, false, false);
}
JavaException(String reason) {
super(reason);
}
public static void main(String[] args) {
int iterations = 1000000;
long create_time_with = 0;
long create_time_without = 0;
long create_string = 0;
for (int i = 0; i < iterations; i++) {
long start = System.nanoTime();
JavaException jex = new JavaException("testing");
long stop = System.nanoTime();
create_time_with += stop - start;
start = System.nanoTime();
JavaException jex2 = new JavaException("testing", 1);
stop = System.nanoTime();
create_time_without += stop - start;
start = System.nanoTime();
String str = new String("testing");
stop = System.nanoTime();
create_string += stop - start;
}
double interval_with = ((double)create_time_with)/1000000;
double interval_without = ((double)create_time_without)/1000000;
double interval_string = ((double)create_string)/1000000;
System.out.printf("Time to create %d String objects: %.2f (ms)\n", iterations, interval_string);
System.out.printf("Time to create %d JavaException objects with stack: %.2f (ms)\n", iterations, interval_with);
System.out.printf("Time to create %d JavaException objects without stack: %.2f (ms)\n", iterations, interval_without);
JavaException jex = new JavaException("testing");
int depth = 14;
int i = depth;
double[] with_stack = new double[20];
double[] without_stack = new double[20];
for(; i > 0 ; --i) {
without_stack[i] = jex.timerLoop(i, iterations, 0)/1000000;
with_stack[i] = jex.timerLoop(i, iterations, 1)/1000000;
}
i = depth;
System.out.printf("|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%%)|\n");
for(; i > 0 ; --i) {
double ratio = (with_stack[i] / (double) without_stack[i]) * 100;
System.out.printf("|%5d| %14.0f| %15.0f| %2.0f (%%)| \n", i + 2, with_stack[i] , without_stack[i], ratio);
//System.out.printf("%d\t%.2f (ms)\n", i, ratio);
}
}
private int thrower(int i, int mode) throws JavaException {
ExArg.time_start[i] = System.nanoTime();
if(mode == 0) { throw new JavaException("without stack", 1); }
throw new JavaException("with stack");
}
private int catcher1(int i, int mode) throws JavaException{
return this.stack_of_calls(i, mode);
}
private long timerLoop(int depth, int iterations, int mode) {
for (int i = 0; i < iterations; i++) {
try {
this.catcher1(depth, mode);
} catch (JavaException e) {
ExArg.time_accum[depth] += (System.nanoTime() - ExArg.time_start[depth]);
}
}
//long stop = System.nanoTime();
return ExArg.time_accum[depth];
}
private int bad_method14(int i, int mode) throws JavaException {
if(i > 0) { this.thrower(i, mode); }
return i;
}
private int bad_method13(int i, int mode) throws JavaException {
if(i == 13) { this.thrower(i, mode); }
return bad_method14(i,mode);
}
private int bad_method12(int i, int mode) throws JavaException{
if(i == 12) { this.thrower(i, mode); }
return bad_method13(i,mode);
}
private int bad_method11(int i, int mode) throws JavaException{
if(i == 11) { this.thrower(i, mode); }
return bad_method12(i,mode);
}
private int bad_method10(int i, int mode) throws JavaException{
if(i == 10) { this.thrower(i, mode); }
return bad_method11(i,mode);
}
private int bad_method9(int i, int mode) throws JavaException{
if(i == 9) { this.thrower(i, mode); }
return bad_method10(i,mode);
}
private int bad_method8(int i, int mode) throws JavaException{
if(i == 8) { this.thrower(i, mode); }
return bad_method9(i,mode);
}
private int bad_method7(int i, int mode) throws JavaException{
if(i == 7) { this.thrower(i, mode); }
return bad_method8(i,mode);
}
private int bad_method6(int i, int mode) throws JavaException{
if(i == 6) { this.thrower(i, mode); }
return bad_method7(i,mode);
}
private int bad_method5(int i, int mode) throws JavaException{
if(i == 5) { this.thrower(i, mode); }
return bad_method6(i,mode);
}
private int bad_method4(int i, int mode) throws JavaException{
if(i == 4) { this.thrower(i, mode); }
return bad_method5(i,mode);
}
protected int bad_method3(int i, int mode) throws JavaException{
if(i == 3) { this.thrower(i, mode); }
return bad_method4(i,mode);
}
private int bad_method2(int i, int mode) throws JavaException{
if(i == 2) { this.thrower(i, mode); }
return bad_method3(i,mode);
}
private int bad_method1(int i, int mode) throws JavaException{
if(i == 1) { this.thrower(i, mode); }
return bad_method2(i,mode);
}
private int stack_of_calls(int i, int mode) throws JavaException{
if(i == 0) { this.thrower(i, mode); }
return bad_method1(i,mode);
}
}
class ExArg {
public static long[] time_start;
public static long[] time_accum;
static {
time_start = new long[20];
time_accum = new long[20];
};
}
이 예의 스택 프레임은 보통 검출되는 프레임에 비해 작습니다.
javap을 사용하여 바이트 코드를 엿볼 수 있습니다.
javap -c -v -constants JavaException.class
즉, 이것은 방법 4에 대한 것입니다.
protected int bad_method3(int, int) throws JavaException;
flags: ACC_PROTECTED
Code:
stack=3, locals=3, args_size=3
0: iload_1
1: iconst_3
2: if_icmpne 12
5: aload_0
6: iload_1
7: iload_2
8: invokespecial #6 // Method thrower:(II)I
11: pop
12: aload_0
13: iload_1
14: iload_2
15: invokespecial #17 // Method bad_method4:(II)I
18: ireturn
LineNumberTable:
line 63: 0
line 64: 12
StackMapTable: number_of_entries = 1
frame_type = 12 /* same */
Exceptions:
throws JavaException
「Exception
a null
는 RPV와 됩니다.throw
★★★★★★★★★★★★★★★★★」try-catch
블록으로 묶다다만, 스택 트레이스를 채우려면 평균 5배의 시간이 걸립니다.
퍼포먼스에 미치는 영향을 설명하기 위해 다음과 같은 벤치마크를 작성했습니다.는 가했 the the the를 추가했다.-Djava.compiler=NONE
[ Run Configuration ]를 선택합니다.트레이스 " "를 했습니다.Exception
스택 프리 컨스트럭터를 활용하는 클래스:
class NoStackException extends Exception{
public NoStackException() {
super("",null,false,false);
}
}
벤치마크 코드는 다음과 같습니다.
public class ExceptionBenchmark {
private static final int NUM_TRIES = 100000;
public static void main(String[] args) {
long throwCatchTime = 0, newExceptionTime = 0, newObjectTime = 0, noStackExceptionTime = 0;
for (int i = 0; i < 30; i++) {
throwCatchTime += throwCatchLoop();
newExceptionTime += newExceptionLoop();
newObjectTime += newObjectLoop();
noStackExceptionTime += newNoStackExceptionLoop();
}
System.out.println("throwCatchTime = " + throwCatchTime / 30);
System.out.println("newExceptionTime = " + newExceptionTime / 30);
System.out.println("newStringTime = " + newObjectTime / 30);
System.out.println("noStackExceptionTime = " + noStackExceptionTime / 30);
}
private static long throwCatchLoop() {
Exception ex = new Exception(); //Instantiated here
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
try {
throw ex; //repeatedly thrown
} catch (Exception e) {
// do nothing
}
}
long stop = System.currentTimeMillis();
return stop - start;
}
private static long newExceptionLoop() {
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
Exception e = new Exception();
}
long stop = System.currentTimeMillis();
return stop - start;
}
private static long newObjectLoop() {
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
Object o = new Object();
}
long stop = System.currentTimeMillis();
return stop - start;
}
private static long newNoStackExceptionLoop() {
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
NoStackException e = new NoStackException();
}
long stop = System.currentTimeMillis();
return stop - start;
}
}
출력:
throwCatchTime = 19
newExceptionTime = 77
newObjectTime = 3
noStackExceptionTime = 15
은, 「」을 작성하는 합니다.NoStackException
과 거의 입니다.Exception
, 「 」, 「 」, 「 Creating 」의 있습니다Exception
스택 트레이스를 채우는 데 걸리는 시간은 약 4배입니다.
질문의 이 부분은...
또 다른 질문 방법은 예외 인스턴스를 하나 만들고 여러 번 던지고 잡으면 매번 새로운 예외를 만드는 것보다 훨씬 빠를까요?
예외를 생성하여 어딘가에 캐싱하면 성능이 향상되는지 여부를 묻는 것 같습니다.네, 포함되었습니다.이는 오브젝트 작성 시 이미 작성되었기 때문에 스택을 끄는 것과 같습니다.
제가 받은 타이밍입니다.이후 주의사항을 읽어주세요...
|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%)|
| 16| 193| 251| 77 (%)|
| 15| 390| 406| 96 (%)|
| 14| 394| 401| 98 (%)|
| 13| 381| 385| 99 (%)|
| 12| 387| 370| 105 (%)|
| 11| 368| 376| 98 (%)|
| 10| 188| 192| 98 (%)|
| 9| 193| 195| 99 (%)|
| 8| 200| 188| 106 (%)|
| 7| 187| 184| 102 (%)|
| 6| 196| 200| 98 (%)|
| 5| 197| 193| 102 (%)|
| 4| 198| 190| 104 (%)|
| 3| 193| 183| 105 (%)|
물론 이 경우 스택트레이스가 오브젝트의 발신지가 아닌 인스턴스화된 위치를 가리킨다는 문제가 있습니다.
@AustinD의 답변을 기점으로 몇 가지 수정을 했습니다.아래에 코드가 있습니다.
하나의 예외 인스턴스가 반복적으로 느려지는 경우를 추가하는 것 외에 정확한 성능 결과를 얻기 위해 컴파일러 최적화를 해제했습니다.는 ㅇㅇㅇㅇㅇㅇㅇㅇ다를 넣었습니다.-Djava.compiler=NONE
이 답변에 따라 VM 인수로 이동합니다. (이해당 항목에서는 구성 실행 → 인수를 편집하여 이 VM 인수를 설정하십시오.)
결과:
new Exception + throw/catch = 643.5
new Exception only = 510.7
throw/catch only = 115.2
new String (benchmark) = 669.8
따라서 예외를 만드는 데 드는 비용은 던지기 + 잡는 비용의 약 5배입니다.컴파일러가 비용의 대부분을 최적화하지 않는다고 가정합니다.
비교를 위해 최적화를 비활성화하지 않은 동일한 테스트 실행을 다음에 나타냅니다.
new Exception + throw/catch = 382.6
new Exception only = 379.5
throw/catch only = 0.3
new String (benchmark) = 15.6
코드:
public class ExceptionPerformanceTest {
private static final int NUM_TRIES = 1000000;
public static void main(String[] args) {
double numIterations = 10;
long exceptionPlusCatchTime = 0, excepTime = 0, strTime = 0, throwTime = 0;
for (int i = 0; i < numIterations; i++) {
exceptionPlusCatchTime += exceptionPlusCatchBlock();
excepTime += createException();
throwTime += catchBlock();
strTime += createString();
}
System.out.println("new Exception + throw/catch = " + exceptionPlusCatchTime / numIterations);
System.out.println("new Exception only = " + excepTime / numIterations);
System.out.println("throw/catch only = " + throwTime / numIterations);
System.out.println("new String (benchmark) = " + strTime / numIterations);
}
private static long exceptionPlusCatchBlock() {
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
try {
throw new Exception();
} catch (Exception e) {
// do nothing
}
}
long stop = System.currentTimeMillis();
return stop - start;
}
private static long createException() {
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
Exception e = new Exception();
}
long stop = System.currentTimeMillis();
return stop - start;
}
private static long createString() {
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
Object o = new String("" + i);
}
long stop = System.currentTimeMillis();
return stop - start;
}
private static long catchBlock() {
Exception ex = new Exception(); //Instantiated here
long start = System.currentTimeMillis();
for (int i = 0; i < NUM_TRIES; i++) {
try {
throw ex; //repeatedly thrown
} catch (Exception e) {
// do nothing
}
}
long stop = System.currentTimeMillis();
return stop - start;
}
}
언급URL : https://stackoverflow.com/questions/36343209/which-part-of-throwing-an-exception-is-expensive
'programing' 카테고리의 다른 글
VueJS 컴포넌트를 Google Map Infowindow로 렌더링 (0) | 2022.07.11 |
---|---|
메서드 내 로컬 변수 상태에 액세스하는 방법 - Vue? (0) | 2022.07.11 |
포인터 '디레퍼런스'란 무슨 뜻입니까? (0) | 2022.07.11 |
미래 목록 대기 중 (0) | 2022.07.11 |
테스트 폴더에 상대 프로젝트 루트 경로를 사용하여 Vue 가져오기 (0) | 2022.07.11 |