programing

Java에서 예외가 성능에 미치는 영향은 무엇입니까?

copyandpastes 2022. 7. 30. 21:43
반응형

Java에서 예외가 성능에 미치는 영향은 무엇입니까?

질문:.Java에서의 예외 처리는 실제로 느립니까?

통념에 의하면, Google의 많은 결과물 뿐만이 아니라, Java의 정상적인 프로그램 플로우에는 예외적인 로직이 사용되어서는 안 된다고 한다.보통 두 가지 이유를 들 수 있습니다.

  1. 이것은 매우 느립니다.일반 코드보다 몇 배 느립니다(이유에 따라 다릅니다).

그리고.

  1. 사람들은 예외적인 코드로 오류만 처리되기를 기대하기 때문에 그것은 지저분하다.

이 질문은 1번 문제입니다.

예를 들어, 이 페이지에서는 Java 예외 처리를 "매우 느리다"라고 설명하고 예외 메시지 문자열 생성과 관련된 느린 속도를 설명합니다. "이 문자열은 느려지는 예외 개체를 만드는 데 사용됩니다.이건 빠르지 않아요.Java에서의 효과적인 예외 처리(Effective Exception Handling in Java)에 따르면 "이 이유는 예외 처리의 객체 생성 측면 때문이며, 따라서 예외 발생 속도가 본질적으로 느립니다.또 다른 이유는 스택트레이스 생성으로 인해 스택트레이스가 느려지는 것입니다.

테스트 결과(32비트 Linux에서 Java 1.6.0_07, Java HotSpot 10.0 사용) 예외 처리 속도가 일반 코드보다 느리지 않습니다.어떤 코드를 실행하는 루프의 메서드를 실행하려고 했습니다.메서드의 마지막에 부울을 사용하여 반환할지 던질지 여부를 나타냅니다.이 방법은 실제 처리와 동일합니다.JVM 워밍업이 원인이라고 생각하여 여러 가지 순서로 방법을 실행해 보고 테스트 시간을 평균해 보았습니다.모든 테스트에서 투구는 빠르지 않더라도 최소한 리턴만큼 빨랐습니다(최대 3.1% 더 빨랐습니다).테스트가 잘못되었을 가능성은 완전히 열려 있지만 코드 샘플, 테스트 비교 또는 지난 1~2년 동안 Java에서 예외 처리가 실제로 느리다는 것을 보여주는 결과는 본 적이 없습니다.

이 과정을 거치게 된 것은 일반적인 제어 로직의 일부로서 예외를 발생시키는 API였습니다.사용법을 수정하고 싶었지만, 지금은 수정하지 못할 수도 있습니다.대신 제가 그들의 미래적 사고를 칭찬해야 할까요?

저스트타임 컴파일의 효율적인 Java 예외 처리에서 저자는 예외가 발생하지 않더라도 예외 핸들러의 존재만으로도 JIT 컴파일러가 코드를 적절하게 최적화하지 못해 속도가 느려진다고 제안합니다.나는 아직 이 이론을 시험해 보지 않았다.

예외 구현 방법에 따라 다릅니다.가장 간단한 방법은 setjmp와 longjmp를 사용하는 것입니다.즉, CPU의 모든 레지스터가 스택에 기록되고(이미 약간의 시간이 소요됨) 다른 데이터를 생성해야 할 수 있습니다.이 모든 것이 이미 시행문에서 일어나고 있습니다.throw 문은 스택을 해제하고 모든 레지스터 값(VM 내의 다른 가능한 값)을 복원해야 합니다.따라서 시도와 투척은 똑같이 느리고, 이는 매우 느립니다.그러나 예외가 발생하지 않으면 대부분의 경우 테스트 블록을 종료하는 데 시간이 걸리지 않습니다(메서드가 있는 경우 자동으로 정리되는 스택에 모든 것이 배치되기 때문에).

Sun과 다른 사람들은 이것이 최적이 아니라고 인식하고 있으며, 물론 VM은 시간이 지남에 따라 점점 더 빨라지고 있습니다.예외를 구현하는 또 다른 방법이 있습니다.이 방법에서는 시행 자체를 번개처럼 빠르게 실행할 수 있습니다(실제로 시행은 전혀 이루어지지 않습니다.클래스가 VM에 의해 로드되었을 때 필요한 모든 작업이 이미 완료되어 있기 때문에 실행 속도가 그다지 느리지 않습니다).어떤 JVM이 이 새롭고 더 나은 기술을 사용하는지 모르겠지만...

...하지만 나중에 하나의 특정 시스템 상의 하나의 JVM에서만 코드가 실행되도록 Java로 작성하시겠습니까?다른 플랫폼이나 다른 JVM 버전(아마도 다른 벤더의 버전)에서 실행할 수 있다면 빠른 구현도 사용한다고 누가 말할까요?빠른 것은 느린 것보다 더 복잡하고 모든 시스템에서 쉽게 가능하지 않습니다.휴대하고 싶어?그렇다면 예외 속도가 빠르다고 생각하지 마십시오.

또한 테스트 블록 내에서 수행하는 작업도 크게 다릅니다.Try Block을 열고 이 Try Block 내에서 어떤 메서드도 호출하지 않으면 Try Block은 매우 빠릅니다.이렇게 하면 JIT는 실제로 투구를 단순한 goto처럼 처리할 수 있습니다.스택 스테이트를 보존할 필요도, 예외가 발생해도 스택을 언인드할 필요도 없습니다(캐치 핸들러로 점프하기만 하면 됩니다).하지만, 이것은 당신이 보통 하는 일이 아닙니다.보통 테스트 블록을 열고 예외를 발생시킬 수 있는 메서드를 호출합니다.또한 try block을 메서드 내에서만 사용해도 다른 메서드는 호출하지 않는 어떤 메서드가 될까요?숫자만 계산되는 건가요?그럼 왜 예외가 필요한 거죠?프로그램 흐름을 조절하는 훨씬 더 우아한 방법이 있습니다.간단한 수학 이외의 거의 모든 것에 대해서는 외부 메서드를 호출해야 합니다.이것에 의해, 로컬의 트라이 블록의 메리트가 이미 파괴되고 있습니다.

다음 테스트 코드를 참조하십시오.

public class Test {
    int value;


    public int getValue() {
        return value;
    }

    public void reset() {
        value = 0;
    }

    // Calculates without exception
    public void method1(int i) {
        value = ((value + i) / i) << 1;
        // Will never be true
        if ((i & 0xFFFFFFF) == 1000000000) {
            System.out.println("You'll never see this!");
        }
    }

    // Could in theory throw one, but never will
    public void method2(int i) throws Exception {
        value = ((value + i) / i) << 1;
        // Will never be true
        if ((i & 0xFFFFFFF) == 1000000000) {
            throw new Exception();
        }
    }

    // This one will regularly throw one
    public void method3(int i) throws Exception {
        value = ((value + i) / i) << 1;
        // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
        // an AND operation between two integers. The size of the number plays
        // no role. AND on 32 BIT always ANDs all 32 bits
        if ((i & 0x1) == 1) {
            throw new Exception();
        }
    }

    public static void main(String[] args) {
        int i;
        long l;
        Test t = new Test();

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            t.method1(i);
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method1 took " + l + " ms, result was " + t.getValue()
        );

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                t.method2(i);
            } catch (Exception e) {
                System.out.println("You'll never see this!");
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method2 took " + l + " ms, result was " + t.getValue()
        );

        l = System.currentTimeMillis();
        t.reset();
        for (i = 1; i < 100000000; i++) {
            try {
                t.method3(i);
            } catch (Exception e) {
                // Do nothing here, as we will get here
            }
        }
        l = System.currentTimeMillis() - l;
        System.out.println(
            "method3 took " + l + " ms, result was " + t.getValue()
        );
    }
}

결과:

method1 took 972 ms, result was 2
method2 took 1003 ms, result was 2
method3 took 66716 ms, result was 2

시행 블럭으로부터의 감속은 배경 공정과 같은 교란 요인을 배제하기에는 너무 작습니다.하지만 그 포획 블록은 모든 것을 죽이고 66배 더 느리게 만들었어요!

말씀드렸듯이 try/catch와 srow를 같은 방법(method3)으로 하면 결과는 그다지 나쁘지 않습니다만, 이것은 특별히 JIT의 최적화입니다.그리고 이 최적화를 사용하더라도 투척 속도는 매우 느립니다.그래서 당신이 여기서 무엇을 하려고 하는지 모르겠지만, 분명히 시도/잡기/던지기보다 더 좋은 방법이 있습니다.

참고로, 나는 Mecki가 했던 실험을 확장했다.

method1 took 1733 ms, result was 2
method2 took 1248 ms, result was 2
method3 took 83997 ms, result was 2
method4 took 1692 ms, result was 2
method5 took 60946 ms, result was 2
method6 took 25746 ms, result was 2

처음 3개는 Mecki와 동일합니다(노트북은 확실히 느립니다).

의 method3의 method4의 method3의 method4의 method3의 method3의 method3의 의 method3의 method3의 new Integer(1) throw new Exception().

의 method3과 method5의 method5의 method3을 .new Exception()던지지 않고.

method6은 method3과 비슷하지만 새로운 예외(인스턴스 변수)를 작성하는 것이 아니라 미리 생성된 예외(인스턴스 변수)를 발생시킵니다.

Java에서는 예외 오브젝트가 생성될 때 발생하는 스택트레이스 수집에 소요되는 시간이 대부분입니다.예외를 발생시키는 실제 비용은 크지만 예외를 만드는 비용보다 상당히 낮습니다.

Aleksey Shipilév는 다양한 조건 조합에서 Java 예외를 벤치마킹하는 매우 철저한 분석을 실시했습니다.

  • 새로 생성된 예외와 미리 생성된 예외
  • 스택 트레이스 활성화 vs 비활성화
  • 스택 트레이스가 요청된 경우와 요청되지 않은 경우
  • 최상위 레벨에서 포착, 각 레벨에서 리트라운, 각 레벨에서 체인/랩
  • 다양한 수준의 Java 콜스택 깊이
  • 인라인 최적화 없음 vs 극한 인라인 vs 디폴트 설정
  • 사용자 정의 필드 읽기/읽지 않음

또한 다양한 수준의 오류 빈도에서 오류 코드를 확인하는 성능과도 비교합니다.

결론은 다음과 같다(그의 글에서 인용한 그대로).

  1. 정말 예외적인 예외는 아름다운 퍼포먼스입니다.설계대로 사용하고, 통상의 코드로 처리되는 예외 없는 케이스의 수가 압도적으로 많은 가운데, 정말로 예외적인 케이스만을 전달할 경우, 예외를 사용하는 것이 퍼포먼스의 우위입니다.

  2. 예외 퍼포먼스 비용에는 2개의 주요 컴포넌트가 있습니다.예외가 인스턴스화된 경우의 스택트레이스 구축과 예외 슬로우 시의 스택 언와인드입니다.

  3. 스택 트레이스 구축 비용은 예외 인스턴스화 시점의 스택 깊이에 비례합니다.이 투척법이 어떤 식으로 불릴지 누가 알기에 이미 나쁜 건가요?스택 트레이스 생성을 끄거나 예외를 캐시해도 퍼포먼스 비용의 이 부분만 삭제할 수 있습니다.

  4. 스택 언와인딩 비용은 컴파일된 코드에서 예외 핸들러를 더 가까이 두는 것이 얼마나 운이 좋은지에 따라 달라집니다.상세한 예외 핸들러 검색을 피하기 위해 코드를 신중하게 구성하면 운이 좋을 수 있습니다.

  5. 두 가지 효과를 모두 제거할 경우 예외에 따른 성능 비용은 현지 지점의 비용입니다.아무리 아름답게 들린다고 해도 예외사항을 일반적인 제어 흐름으로 사용해야 한다는 의미는 아닙니다. 왜냐하면 이 경우 컴파일러를 최적화할 수 있기 때문입니다.예외 빈도가 실제 예외를 발생시킬 때 발생할 수 있는 불운한 비용을 상각하는 정말 예외적인 경우에만 사용해야 합니다.

  6. 10^-4의 주파수는 10^-4인 처럼 보이지만 예외는 충분히 예외적이다.물론 이는 예외 자체의 중량감, 예외 핸들러에서 수행되는 정확한 액션 등에 따라 달라집니다.

으로 예외가 않을 에 예외 에는 예외 를 사용하는 보다 빠르게 할 수 .if the. 가 있습니다.그 전문은 읽을 가치가 있다.

유감스럽게도, 제 답변은 여기에 올리기에는 너무 길어요.자세한 내용은 http://www.fuwjax.com/how-slow-are-java-exceptions/을 참조해 주십시오.

여기서의 진짜 질문은 "'실패하지 않는 코드'에 비해 '예외로 보고되는 장애'는 얼마나 느리느냐"가 아닙니다.이러한 응답은 받아들여진 응답으로 믿을 수 있습니다.대신 "예외로 보고된 장애는 다른 방법으로 보고된 장애에 비해 얼마나 느립니까?"라고 질문해야 합니다.일반적으로 장애를 보고하는 다른 두 가지 방법은 sentinel 값 또는 결과 래퍼를 사용하는 것입니다.

Sentinel 값은 성공한 경우 클래스를 반환하고 실패한 경우 클래스를 반환하는 시도입니다.예외를 던지는 대신 예외를 반환하는 것으로 생각할 수 있습니다.이를 위해서는 성공 오브젝트를 가진 공유 부모 클래스가 필요하며 성공 또는 실패 정보를 얻기 위해 "인스턴스" 체크와 커플 캐스팅을 수행해야 합니다.

Sentinel 값이 예외보다 빠르지만 약 2배 정도밖에 되지 않는 것으로 나타났습니다.많은 것 같지만, 이 2배로는 구현 차이에 따른 비용만 커버할 수 있습니다.실제로는 실패할 가능성이 있는 방법이 이 페이지의 샘플 코드와 같이 소수의 산술 연산자보다 훨씬 더 흥미롭기 때문에 계수는 훨씬 낮습니다.

반면 결과 포장지는 타입의 안전을 전혀 희생하지 않습니다.성공 및 실패 정보는 단일 클래스로 정리됩니다.따라서 "instance of" 대신 성공 및 실패 개체 모두에 대해 "isSuccess()"와 getters를 제공합니다.그러나 결과 개체는 예외를 사용하는 경우보다 약 2배 느립니다.매번 새로운 래퍼 오브젝트를 작성하는 것은 예외를 설정하는 것보다 훨씬 비용이 많이 드는 것으로 나타났습니다.

또한 예외는 메서드가 실패할 수 있음을 나타내는 방법을 제공하는 언어입니다.API만으로 (대부분) 동작할 것으로 예상되는 메서드와 실패를 보고할 것으로 예상되는 메서드를 구분할 수 있는 방법은 없습니다.

예외는 감시병보다 안전하고, 결과 객체보다 빠르고, 어느 쪽보다 덜 놀랍습니다.if/else를 try/catch로 대체하자는 것은 아니지만, 비즈니스 논리에서도 예외는 오류를 보고하는 올바른 방법입니다.

즉, 지금까지 퍼포먼스에 큰 영향을 미치는 가장 빈번한 두 가지 방법은 불필요한 오브젝트 작성과 네스트 루프 작성입니다.예외를 만들 것인지 아니면 예외를 만들지 않을 것인지 선택할 수 있는 경우 예외를 만들지 마십시오.예외를 가끔 작성할지 아니면 항상 다른 개체를 작성할지 선택할 수 있는 경우 예외를 만듭니다.

@Mecki @incarnate의 답변을 확장했습니다.Java의 스택 트레이스를 채우지 않습니다.

7 7+를 사용할 수 .Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace)Java6의 경우 이 질문에 대한 답변을 참조하십시오.

// This one will regularly throw one
public void method4(int i) throws NoStackTraceThrowable {
    value = ((value + i) / i) << 1;
    // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
    // an AND operation between two integers. The size of the number plays
    // no role. AND on 32 BIT always ANDs all 32 bits
    if ((i & 0x1) == 1) {
        throw new NoStackTraceThrowable();
    }
}

// This one will regularly throw one
public void method5(int i) throws NoStackTraceRuntimeException {
    value = ((value + i) / i) << 1;
    // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
    // an AND operation between two integers. The size of the number plays
    // no role. AND on 32 BIT always ANDs all 32 bits
    if ((i & 0x1) == 1) {
        throw new NoStackTraceRuntimeException();
    }
}

public static void main(String[] args) {
    int i;
    long l;
    Test t = new Test();

    l = System.currentTimeMillis();
    t.reset();
    for (i = 1; i < 100000000; i++) {
        try {
            t.method4(i);
        } catch (NoStackTraceThrowable e) {
            // Do nothing here, as we will get here
        }
    }
    l = System.currentTimeMillis() - l;
    System.out.println( "method4 took " + l + " ms, result was " + t.getValue() );


    l = System.currentTimeMillis();
    t.reset();
    for (i = 1; i < 100000000; i++) {
        try {
            t.method5(i);
        } catch (RuntimeException e) {
            // Do nothing here, as we will get here
        }
    }
    l = System.currentTimeMillis() - l;
    System.out.println( "method5 took " + l + " ms, result was " + t.getValue() );
}

Java 1.6.0_45, Core i7, 8GB RAM에서의 출력:

method1 took 883 ms, result was 2
method2 took 882 ms, result was 2
method3 took 32270 ms, result was 2 // throws Exception
method4 took 8114 ms, result was 2 // throws NoStackTraceThrowable
method5 took 8086 ms, result was 2 // throws NoStackTraceRuntimeException

따라서 여전히 값을 반환하는 메서드가 예외를 던지는 메서드에 비해 더 빠릅니다.IMHO, 성공 흐름과 오류 흐름 모두에 대해 리턴 유형만 사용하여 클리어 API를 설계할 수 없습니다.스택 트레이스를 사용하지 않고 예외를 슬로우하는 메서드는 일반 예외보다 4~5배 빠릅니다.

편집: NoStackTraceThroughable.java 감사 @Greg

public class NoStackTraceThrowable extends Throwable { 
    public NoStackTraceThrowable() { 
        super("my special throwable", null, false, false);
    }
}

얼마 전 저는 (1) Integer.parseInt()를 호출하여 예외를 포착하거나 (2) 문자열과 regex 및 call parseInt()를 일치시키는 두 가지 접근 방식을 사용하여 문자열을 int로 변환하는 상대적인 성능을 테스트하는 클래스를 작성했습니다.가능한 한 효율적으로 regex를 사용(즉, 루프를 삽입하기 전에 Pattern 객체와 Matcher 객체를 작성)했으며 예외에서 스택 트레이스를 인쇄하거나 저장하지 않았습니다.

10,000개의 문자열 리스트의 경우 모두 유효한 수치일 경우 parseInt() 접근법은 regex 접근법의 4배였습니다.그러나 문자열의 80%만 유효한 경우 regex는 parseInt()보다 2배 빠릅니다.20%가 유효하면, 즉 예외가 느려져 80%가 검출된 경우 regex의 속도는 parseInt()의 약 20배였습니다.

regex 접근법은 유효한 문자열을 2회(일치 및 parseInt()) 처리하는 것을 생각하면 그 결과에 놀랐습니다.하지만 예외를 던지고 잡는 것만으로 만회할 수 있는 게 아닙니다.이런 상황은 현실에서 자주 일어나지 않을 것 같지만, 만약 일어난다면, 당신은 절대 예외 포착 기술을 사용해서는 안 된다.다만, 유저 입력등의 검증만을 실시하는 경우는, 반드시 parseInt() 어프로치를 사용해 주세요.

이러한 토픽이 관련되어 있는지는 모르겠지만, 한 번은 현재의 스레드의 스택 트레이스에 의존하는 트릭을 실장하고 싶다고 생각하고 있었습니다.인스턴스화된 수업에서 인스턴스화를 촉발시킨 메서드의 이름을 알고 싶었다(예, 아이디어는 미쳤고 완전히 포기했다). 저는 '콜링'이 '발신'이라는 했습니다.Thread.currentThread().getStackTrace()(원어민 때문에) 매우 느리다dumpThreads을 사용법

Java는Throwable대응하여 메서드가 있습니다.fillInStackTracecatch앞에서 설명한 블록은 이 메서드의 실행을 트리거합니다.

하지만 다른 이야기를 해드리죠

에서는 일부 기능이 Scala를 사용하여 .ControlThrowable)Throwable 그 위에 덧씌워집니다.fillInStackTrace다음과 같은 방법으로.

override def fillInStackTrace(): Throwable = this

그래서 위의 테스트를 적용했습니다(주기는 10회 줄었고, 기계는 조금 느립니다).

class ControlException extends ControlThrowable

class T {
  var value = 0

  def reset = {
    value = 0
  }

  def method1(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0xfffffff) == 1000000000) {
      println("You'll never see this!")
    }
  }

  def method2(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0xfffffff) == 1000000000) {
      throw new Exception()
    }
  }

  def method3(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0x1) == 1) {
      throw new Exception()
    }
  }

  def method4(i: Int) = {
    value = ((value + i) / i) << 1
    if ((i & 0x1) == 1) {
      throw new ControlException()
    }
  }
}

class Main {
  var l = System.currentTimeMillis
  val t = new T
  for (i <- 1 to 10000000)
    t.method1(i)
  l = System.currentTimeMillis - l
  println("method1 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method2(i)
  } catch {
    case _ => println("You'll never see this")
  }
  l = System.currentTimeMillis - l
  println("method2 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method4(i)
  } catch {
    case _ => // do nothing
  }
  l = System.currentTimeMillis - l
  println("method4 took " + l + " ms, result was " + t.value)

  t.reset
  l = System.currentTimeMillis
  for (i <- 1 to 10000000) try {
    t.method3(i)
  } catch {
    case _ => // do nothing
  }
  l = System.currentTimeMillis - l
  println("method3 took " + l + " ms, result was " + t.value)

}

결과는 다음과 같습니다.

method1 took 146 ms, result was 2
method2 took 159 ms, result was 2
method4 took 1551 ms, result was 2
method3 took 42492 ms, result was 2

른른른, 른른면면면면면의 method3 ★★★★★★★★★★★★★★★★★」method4하다 응,method4 method1 ★★★★★★★★★★★★★★★★★」method2단, 그 차이는 훨씬 더 받아들일 수 있습니다.

첫 번째 기사는 콜스택을 통과하여 스택트레이스를 작성하는 행위를 고가의 부분으로 언급하고 있다고 생각합니다.두 번째 기사에는 기재되어 있지 않지만 오브젝트 작성에서는 그것이 가장 고가의 부분이라고 생각합니다.John Rose는 예외 처리 속도를 높이기 위한 다양한 기술을 기술하고 있습니다(예외, 스택 트레이스 없는 예외의 사전 할당 및 재사용 등).

하지만 여전히 - 나는 이것이 단지 필요악이자 최후의 수단으로 여겨져야 한다고 생각한다.John이 이렇게 하는 이유는 JVM에서 아직 사용할 수 없는 다른 언어로 된 기능을 에뮬레이트하기 위해서입니다.제어 흐름에 예외를 사용하는 습관을 들이지 마십시오.특히 퍼포먼스를 위해서가 아닙니다!당신이 #2에서 언급했듯이, 당신은 이런 식으로 코드의 심각한 버그를 가릴 위험을 감수해야 하며, 새로운 프로그래머를 위한 유지보수는 더 어려워질 것입니다.

Java의 마이크로벤치마크는 특히 JIT 영역에 들어갔을 때 정확성이 놀라울 정도로 낮기 때문에 실제로 예외를 사용하는 것이 "복귀"보다 더 빠른지 의심스럽다.예를 들어, 테스트에서 2~5개의 스택 프레임이 있을 것으로 생각됩니다.JBoss에 의해 전개된 JSF 컴포넌트에 의해 코드가 호출된다고 가정합니다.몇 페이지 길이의 스택트레이스를 사용할 수 있습니다.

테스트 코드 좀 올려주시겠어요?

JVM 1.5에서 몇 가지 성능 테스트를 해봤는데 예외 사용 속도가 최소 2배 느렸습니다.평균:예외적인 경우를 제외하고, 3배 이상 작은 방법으로 실행 시간이 3배 이상 증가했습니다.예외를 포착해야 하는 작은 루프에서는 셀프타임이 2배 증가했습니다.

마이크로 벤치마크뿐만 아니라 생산 코드에서도 비슷한 수치를 본 적이 있습니다.

자주 호출되는 항목에는 예외를 사용해서는 안 됩니다.초당 수천 개의 예외를 두는 것은 거대한 병목을 야기할 것이다.

예를 들어 "Integer"를 사용합니다.ParseInt(...)매우 큰 텍스트 파일에서 모든 잘못된 값을 찾을 수 있습니다. 매우 잘못된 생각입니다.(제품 코드에서 이 유틸리티 방식의 킬 퍼포먼스를 본 적이 있습니다)

예외를 사용하여 사용자 GUI 폼에 잘못된 값을 보고합니다.퍼포먼스 관점에서 보면 그다지 나쁘지 않을 수 있습니다.

이것이 좋은 디자인 프랙티스인지 아닌지에 관계없이, 저는 에러가 정상/예상된 경우 반환값을 사용합니다.비정상인 경우 예외를 사용합니다.예를 들어, 사용자 입력을 읽으면 잘못된 값이 정상입니다. 오류 코드를 사용하십시오.내부 유틸리티 함수에 값을 전달할 때 잘못된 값은 호출 코드로 필터링해야 합니다(예외 사용).

Java 및 C#에서의 예외 퍼포먼스에는 부족한 점이 많습니다.

프로그래머로서, 이는 단순히 실질적인 성능상의 이유로 "예외는 드물게 발생해야 한다"는 규칙에 따라 생활하도록 강요합니다.

하지만, 컴퓨터 과학자로서, 우리는 이 문제가 있는 상태에 대해 저항해야 한다.함수를 작성하는 사람은 함수를 호출하는 빈도나 성공 또는 실패 가능성이 높은지 여부를 모르는 경우가 많습니다.발신자만이 이 정보를 가지고 있습니다.예외를 회피하려고 하면 API idom이 불분명한 경우가 있습니다.이 경우 클린하지만 느린 예외 버전만 있는 경우도 있고, 고속이지만 투박한 반환값 오류가 있는 경우도 있습니다.또한 이 두 버전 모두로 끝나는 경우도 있습니다.라이브러리 실장자는 두 가지 버전의 API를 작성하고 유지해야 하며, 호출자는 각 상황에서 사용할 두 가지 버전 중 하나를 결정해야 합니다.

이건 좀 엉망진창이야.예외가 더 나은 성능을 발휘한다면, 우리는 이러한 투박한 관용구를 피하고 예외를 사용하기로 되어 있는 그대로 사용할 수 있을 것이다.구조화된 오류 반환 기능으로 사용됩니다.

반환값에 가까운 기술을 사용하여 예외 메커니즘을 구현하여 반환값에 가까운 성능을 얻을 수 있도록 하고 싶습니다.퍼포먼스에 민감한 코드로 되돌리는 것이기 때문입니다.

다음은 예외 퍼포먼스와 에러 반환값 퍼포먼스를 비교한 코드샘플입니다.

퍼블릭 클래스 TestIt {

int value;


public int getValue() {
    return value;
}

public void reset() {
    value = 0;
}

public boolean baseline_null(boolean shouldfail, int recurse_depth) {
    if (recurse_depth <= 0) {
        return shouldfail;
    } else {
        return baseline_null(shouldfail,recurse_depth-1);
    }
}

public boolean retval_error(boolean shouldfail, int recurse_depth) {
    if (recurse_depth <= 0) {
        if (shouldfail) {
            return false;
        } else {
            return true;
        }
    } else {
        boolean nested_error = retval_error(shouldfail,recurse_depth-1);
        if (nested_error) {
            return true;
        } else {
            return false;
        }
    }
}

public void exception_error(boolean shouldfail, int recurse_depth) throws Exception {
    if (recurse_depth <= 0) {
        if (shouldfail) {
            throw new Exception();
        }
    } else {
        exception_error(shouldfail,recurse_depth-1);
    }

}

public static void main(String[] args) {
    int i;
    long l;
    TestIt t = new TestIt();
    int failures;

    int ITERATION_COUNT = 100000000;


    // (0) baseline null workload
    for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
        for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
            int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

            failures = 0;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                t.baseline_null(shoulderror,recurse_depth);
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("baseline: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    recurse_depth, exception_freq, failures,elapsed_time);
        }
    }


    // (1) retval_error
    for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
        for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
            int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

            failures = 0;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                if (!t.retval_error(shoulderror,recurse_depth)) {
                    failures++;
                }
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("retval_error: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    recurse_depth, exception_freq, failures,elapsed_time);
        }
    }

    // (2) exception_error
    for (int recurse_depth = 2; recurse_depth <= 10; recurse_depth+=3) {
        for (float exception_freq = 0.0f; exception_freq <= 1.0f; exception_freq += 0.25f) {            
            int EXCEPTION_MOD = (exception_freq == 0.0f) ? ITERATION_COUNT+1 : (int)(1.0f / exception_freq);            

            failures = 0;
            long start_time = System.currentTimeMillis();
            t.reset();              
            for (i = 1; i < ITERATION_COUNT; i++) {
                boolean shoulderror = (i % EXCEPTION_MOD) == 0;
                try {
                    t.exception_error(shoulderror,recurse_depth);
                } catch (Exception e) {
                    failures++;
                }
            }
            long elapsed_time = System.currentTimeMillis() - start_time;
            System.out.format("exception_error: recurse_depth %s, exception_freqeuncy %s (%s), time elapsed %s ms\n",
                    recurse_depth, exception_freq, failures,elapsed_time);              
        }
    }
}

}

결과는 다음과 같습니다.

baseline: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 683 ms
baseline: recurse_depth 2, exception_freqeuncy 0.25 (0), time elapsed 790 ms
baseline: recurse_depth 2, exception_freqeuncy 0.5 (0), time elapsed 768 ms
baseline: recurse_depth 2, exception_freqeuncy 0.75 (0), time elapsed 749 ms
baseline: recurse_depth 2, exception_freqeuncy 1.0 (0), time elapsed 731 ms
baseline: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 923 ms
baseline: recurse_depth 5, exception_freqeuncy 0.25 (0), time elapsed 971 ms
baseline: recurse_depth 5, exception_freqeuncy 0.5 (0), time elapsed 982 ms
baseline: recurse_depth 5, exception_freqeuncy 0.75 (0), time elapsed 947 ms
baseline: recurse_depth 5, exception_freqeuncy 1.0 (0), time elapsed 937 ms
baseline: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1154 ms
baseline: recurse_depth 8, exception_freqeuncy 0.25 (0), time elapsed 1149 ms
baseline: recurse_depth 8, exception_freqeuncy 0.5 (0), time elapsed 1133 ms
baseline: recurse_depth 8, exception_freqeuncy 0.75 (0), time elapsed 1117 ms
baseline: recurse_depth 8, exception_freqeuncy 1.0 (0), time elapsed 1116 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 742 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.25 (24999999), time elapsed 743 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.5 (49999999), time elapsed 734 ms
retval_error: recurse_depth 2, exception_freqeuncy 0.75 (99999999), time elapsed 723 ms
retval_error: recurse_depth 2, exception_freqeuncy 1.0 (99999999), time elapsed 728 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 920 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.25 (24999999), time elapsed 1121   ms
retval_error: recurse_depth 5, exception_freqeuncy 0.5 (49999999), time elapsed 1037 ms
retval_error: recurse_depth 5, exception_freqeuncy 0.75 (99999999), time elapsed 1141   ms
retval_error: recurse_depth 5, exception_freqeuncy 1.0 (99999999), time elapsed 1130 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1218 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.25 (24999999), time elapsed 1334  ms
retval_error: recurse_depth 8, exception_freqeuncy 0.5 (49999999), time elapsed 1478 ms
retval_error: recurse_depth 8, exception_freqeuncy 0.75 (99999999), time elapsed 1637 ms
retval_error: recurse_depth 8, exception_freqeuncy 1.0 (99999999), time elapsed 1655 ms
exception_error: recurse_depth 2, exception_freqeuncy 0.0 (0), time elapsed 726 ms
exception_error: recurse_depth 2, exception_freqeuncy 0.25 (24999999), time elapsed 17487   ms
exception_error: recurse_depth 2, exception_freqeuncy 0.5 (49999999), time elapsed 33763   ms
exception_error: recurse_depth 2, exception_freqeuncy 0.75 (99999999), time elapsed 67367   ms
exception_error: recurse_depth 2, exception_freqeuncy 1.0 (99999999), time elapsed 66990 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.0 (0), time elapsed 924 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.25 (24999999), time elapsed 23775  ms
exception_error: recurse_depth 5, exception_freqeuncy 0.5 (49999999), time elapsed 46326 ms
exception_error: recurse_depth 5, exception_freqeuncy 0.75 (99999999), time elapsed 91707 ms
exception_error: recurse_depth 5, exception_freqeuncy 1.0 (99999999), time elapsed 91580 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.0 (0), time elapsed 1144 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.25 (24999999), time elapsed 30440 ms
exception_error: recurse_depth 8, exception_freqeuncy 0.5 (49999999), time elapsed 59116   ms
exception_error: recurse_depth 8, exception_freqeuncy 0.75 (99999999), time elapsed 116678 ms
exception_error: recurse_depth 8, exception_freqeuncy 1.0 (99999999), time elapsed 116477 ms

반환값을 체크 및 전파하면 베이스라인 늘콜에 비해 비용이 약간 추가되며 이 비용은 콜 심도에 비례합니다.콜 체인 깊이 8에서는 error-return-value 체크버전은 반환값을 체크하지 않은 베이스라인 버전보다 약 27% 느렸습니다.

이에 비해 예외 퍼포먼스는 콜 심도의 함수가 아니라 예외 빈도에 의한 것입니다.단, 예외 빈도가 증가함에 따라 감도가 훨씬 더 극적으로 이루어집니다.오류 빈도가 25%에 불과할 때 코드가 24배 느리게 실행되었습니다.오류 빈도가 100%일 경우 예외 버전은 거의 100 TIMES 느립니다.

이는 아마도 예외 구현에서 잘못된 트레이드오프를 수행하고 있음을 시사합니다.예외는 비용이 많이 드는 스토킹 워크를 회피하거나 컴파일러가 지원하는 수익률 체크로 완전히 전환함으로써 더 빨라질 수 있습니다.코드가 빨리 실행되기를 원할 때, 우리는 그들을 피할 수 밖에 없습니다.

HotSpot은 시스템에 의해 생성된 예외에 대한 예외 코드를 모두 인라인에 있는 한 삭제할 수 있습니다.단, 명시적으로 작성된 예외 및 삭제되지 않은 예외는 스택트레이스 작성에 많은 시간을 소비합니다.「」를 .fillInStackTrace퍼포먼스에 어떤 영향을 미치는지 확인하세요.

예외 발생 속도가 느리지 않더라도 일반 프로그램 흐름에 대해 예외를 발생시키는 것은 여전히 좋지 않습니다.이런 식으로 쓰면 GOTO와 비슷해...

그렇다고 해서 답이 되는 건 아닌 것 같아요.예외를 느리게 던지는 '기존'의 통념은 이전 자바 버전(< 1.4)에서는 사실이었던 것 같습니다.예외를 만들려면 VM이 전체 스택 트레이스를 생성해야 합니다.그 이후로 VM은 많은 변화가 있어 작업 속도를 향상시키고 있습니다.이것은 아마 개선되고 있는 영역 중 하나일 것입니다.

예를 들어 Integer.parseInt와 다음 메서드를 비교해 보겠습니다.이 메서드는 데이터를 구문 분석할 수 없는 경우 예외를 발생시키지 않고 기본값을 반환합니다.

  public static int parseUnsignedInt(String s, int defaultValue) {
    final int strLength = s.length();
    if (strLength == 0)
      return defaultValue;
    int value = 0;
    for (int i=strLength-1; i>=0; i--) {
      int c = s.charAt(i);
      if (c > 47 && c < 58) {
        c -= 48;
        for (int j=strLength-i; j!=1; j--)
          c *= 10;
        value += c;
      } else {
        return defaultValue;
      }
    }
    return value < 0 ? /* übergebener wert > Integer.MAX_VALUE? */ defaultValue : value;
  }

두 가지 방법을 "유효한" 데이터에 적용하기만 하면 두 가지 방법은 모두 거의 동일한 속도로 작동합니다(Integer.parseInt가 더 복잡한 데이터를 처리할 수 있더라도).그러나 비활성 데이터를 구문 분석하려고 하면("abc" 1.000.000번 구문 분석 등) 성능 차이가 필수적입니다.

첨부된 코드를 사용하여 JDK 15에서 @Mecki 테스트 케이스에 대해 전혀 다른 결과를 얻을 수 있습니다.이 방법에서는 기본적으로 코드가 5개의 루프로 실행되며 첫 번째 루프는 VM을 워밍업할 시간을 주기 위해 약간 짧아집니다.

결과:

Loop 1 10000 cycles
method1 took 1 ms, result was 2
method2 took 0 ms, result was 2
method3 took 22 ms, result was 2
method4 took 22 ms, result was 2
method5 took 24 ms, result was 2
Loop 2 10000000 cycles
method1 took 39 ms, result was 2
method2 took 39 ms, result was 2
method3 took 1558 ms, result was 2
method4 took 1640 ms, result was 2
method5 took 1717 ms, result was 2
Loop 3 10000000 cycles
method1 took 49 ms, result was 2
method2 took 48 ms, result was 2
method3 took 126 ms, result was 2
method4 took 88 ms, result was 2
method5 took 87 ms, result was 2
Loop 4 10000000 cycles
method1 took 34 ms, result was 2
method2 took 34 ms, result was 2
method3 took 33 ms, result was 2
method4 took 98 ms, result was 2
method5 took 58 ms, result was 2
Loop 5 10000000 cycles
method1 took 34 ms, result was 2
method2 took 33 ms, result was 2
method3 took 33 ms, result was 2
method4 took 48 ms, result was 2
method5 took 49 ms, result was 2
package hs.jfx.eventstream.api;

public class Snippet {
  int value;


  public int getValue() {
      return value;
  }

  public void reset() {
      value = 0;
  }

  // Calculates without exception
  public void method1(int i) {
      value = ((value + i) / i) << 1;
      // Will never be true
      if ((i & 0xFFFFFFF) == 1000000000) {
          System.out.println("You'll never see this!");
      }
  }

  // Could in theory throw one, but never will
  public void method2(int i) throws Exception {
      value = ((value + i) / i) << 1;
      // Will never be true
      if ((i & 0xFFFFFFF) == 1000000000) {
          throw new Exception();
      }
  }

  private static final NoStackTraceRuntimeException E = new NoStackTraceRuntimeException();

  // This one will regularly throw one
  public void method3(int i) throws NoStackTraceRuntimeException {
      value = ((value + i) / i) << 1;
      // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
      // an AND operation between two integers. The size of the number plays
      // no role. AND on 32 BIT always ANDs all 32 bits
      if ((i & 0x1) == 1) {
          throw E;
      }
  }

  // This one will regularly throw one
  public void method4(int i) throws NoStackTraceThrowable {
      value = ((value + i) / i) << 1;
      // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
      // an AND operation between two integers. The size of the number plays
      // no role. AND on 32 BIT always ANDs all 32 bits
      if ((i & 0x1) == 1) {
          throw new NoStackTraceThrowable();
      }
  }

  // This one will regularly throw one
  public void method5(int i) throws NoStackTraceRuntimeException {
      value = ((value + i) / i) << 1;
      // i & 1 is equally fast to calculate as i & 0xFFFFFFF; it is both
      // an AND operation between two integers. The size of the number plays
      // no role. AND on 32 BIT always ANDs all 32 bits
      if ((i & 0x1) == 1) {
          throw new NoStackTraceRuntimeException();
      }
  }

  public static void main(String[] args) {
    for(int k = 0; k < 5; k++) {
      int cycles = 10000000;
      if(k == 0) {
        cycles = 10000;
        try {
          Thread.sleep(500);
        }
        catch(InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
      System.out.println("Loop " + (k + 1) + " " + cycles + " cycles");
      int i;
      long l;
      Snippet t = new Snippet();

      l = System.currentTimeMillis();
      t.reset();
      for (i = 1; i < cycles; i++) {
          t.method1(i);
      }
      l = System.currentTimeMillis() - l;
      System.out.println(
          "method1 took " + l + " ms, result was " + t.getValue()
      );

      l = System.currentTimeMillis();
      t.reset();
      for (i = 1; i < cycles; i++) {
          try {
              t.method2(i);
          } catch (Exception e) {
              System.out.println("You'll never see this!");
          }
      }
      l = System.currentTimeMillis() - l;
      System.out.println(
          "method2 took " + l + " ms, result was " + t.getValue()
      );

      l = System.currentTimeMillis();
      t.reset();
      for (i = 1; i < cycles; i++) {
          try {
              t.method3(i);
          } catch (NoStackTraceRuntimeException e) {
            // always comes here
          }
      }
      l = System.currentTimeMillis() - l;
      System.out.println(
          "method3 took " + l + " ms, result was " + t.getValue()
      );


      l = System.currentTimeMillis();
      t.reset();
      for (i = 1; i < cycles; i++) {
          try {
              t.method4(i);
          } catch (NoStackTraceThrowable e) {
            // always comes here
          }
      }
      l = System.currentTimeMillis() - l;
      System.out.println( "method4 took " + l + " ms, result was " + t.getValue() );


      l = System.currentTimeMillis();
      t.reset();
      for (i = 1; i < cycles; i++) {
          try {
              t.method5(i);
          } catch (RuntimeException e) {
            // always comes here
          }
      }
      l = System.currentTimeMillis() - l;
      System.out.println( "method5 took " + l + " ms, result was " + t.getValue() );
    }
  }

  public static class NoStackTraceRuntimeException extends RuntimeException {
    public NoStackTraceRuntimeException() {
        super("my special throwable", null, false, false);
    }
  }

  public static class NoStackTraceThrowable extends Throwable {
    public NoStackTraceThrowable() {
        super("my special throwable", null, false, false);
    }
  }
}

예외 퍼포먼스에 대한 훌륭한 게시물은 다음과 같습니다.

https://shipilev.net/blog/2014/exceptional-performance/

인스턴스화와 기존 재사용(스택 트레이스 포함, 사용 안 함 등)

Benchmark                            Mode   Samples         Mean   Mean error  Units

dynamicException                     avgt        25     1901.196       14.572  ns/op
dynamicException_NoStack             avgt        25       67.029        0.212  ns/op
dynamicException_NoStack_UsedData    avgt        25       68.952        0.441  ns/op
dynamicException_NoStack_UsedStack   avgt        25      137.329        1.039  ns/op
dynamicException_UsedData            avgt        25     1900.770        9.359  ns/op
dynamicException_UsedStack           avgt        25    20033.658      118.600  ns/op

plain                                avgt        25        1.259        0.002  ns/op
staticException                      avgt        25        1.510        0.001  ns/op
staticException_NoStack              avgt        25        1.514        0.003  ns/op
staticException_NoStack_UsedData     avgt        25        4.185        0.015  ns/op
staticException_NoStack_UsedStack    avgt        25       19.110        0.051  ns/op
staticException_UsedData             avgt        25        4.159        0.007  ns/op
staticException_UsedStack            avgt        25       25.144        0.186  ns/op

스택 트레이스의 깊이에 따라 다릅니다.

Benchmark        Mode   Samples         Mean   Mean error  Units

exception_0000   avgt        25     1959.068       30.783  ns/op
exception_0001   avgt        25     1945.958       12.104  ns/op
exception_0002   avgt        25     2063.575       47.708  ns/op
exception_0004   avgt        25     2211.882       29.417  ns/op
exception_0008   avgt        25     2472.729       57.336  ns/op
exception_0016   avgt        25     2950.847       29.863  ns/op
exception_0032   avgt        25     4416.548       50.340  ns/op
exception_0064   avgt        25     6845.140       40.114  ns/op
exception_0128   avgt        25    11774.758       54.299  ns/op
exception_0256   avgt        25    21617.526      101.379  ns/op
exception_0512   avgt        25    42780.434      144.594  ns/op
exception_1024   avgt        25    82839.358      291.434  ns/op

기타 상세 정보(JIT의 x64 어셈블러 포함)는 원본 블로그 게시물을 참조하십시오.

즉, 예외(xD) 때문에 휴지 상태/스프링/etc-EE 똥이 느립니다.

하고 앱 를 「오류」로( 「오류 반환」).return

위의 @Mecki의 답변을 변경하여 method1이 호출 메서드의 부울과 체크를 반환하도록 했습니다.예외를 0으로 대체할 수는 없기 때문입니다.두 번의 실행 후에도 method1이 여전히 method2만큼 빠르거나 빨랐습니다.

다음은 코드의 스냅샷입니다.

// Calculates without exception
public boolean method1(int i) {
    value = ((value + i) / i) << 1;
    // Will never be true
    return ((i & 0xFFFFFFF) == 1000000000);

}
....
   for (i = 1; i < 100000000; i++) {
            if (t.method1(i)) {
                System.out.println("Will never be true!");
            }
    }

및 결과:

실행 1

method1 took 841 ms, result was 2
method2 took 841 ms, result was 2
method3 took 85058 ms, result was 2

실행 2

method1 took 821 ms, result was 2
method2 took 838 ms, result was 2
method3 took 85929 ms, result was 2

예외 속도와 프로그래밍 방식으로 데이터를 확인하는 것에 대한 제 의견입니다.

많은 클래스에는 String-to-value 변환기(스캐너/파서), 존중되고 잘 알려진 라이브러리도 있습니다.

보통 폼이 있다

class Example {
public static Example Parse(String input) throws AnyRuntimeParsigException
...
}

예외명은 예에 불과하며, 보통 체크박스가 꺼집니다.따라서 선언은 내 그림일 뿐입니다.

때때로 두 번째 형식이 존재합니다.

public static Example Parse(String input, Example defaultValue)

던지지 않다

두 번째 인스톨을 사용할 수 없는 경우(또는 프로그래머가 너무 적은 수의 문서를 읽고 첫 번째 인스톨만 사용하는 경우), 정규식을 사용하여 코드를 작성합니다.정규 표현은 쿨하고 정치적으로 올바른 표현 등:

Xxxxx.regex(".....pattern", src);
if(ImTotallySure)
{
  Example v = Example.Parse(src);
}

예외적인 비용은 들지 않습니다.단, 정규 표현에 드는 비용은 항상 매우 높지만 예외 비용이 적은 경우도 있습니다.

나는 거의 항상 그런 맥락에 사용합니다.

try { parse } catch(ParsingException ) // concrete exception from javadoc
{
}

stacktrace 등 분석이 없었다면, 나는 후에 당신의 강의 조용한 속도 믿는다.

되지 말라 두려워하예외.

왜 예외는 정상보다 수익 느려야?

너만 터미널로 파일로 저장하거나 유사한 것은 stacktrace을 싣지 말아, catch-block 다른 code-blocks에 비해 더 많은 일을 하지 않는다.왜"( 새로운 my_cool_error을 던지)" 그렇게 느릴 그래서, 나는 상상할 수 없다.

좋은 질문과 나는 이 주제에 대한 더 많은 정보!

언급URL:https://stackoverflow.com/questions/299068/what-are-the-effects-of-exceptions-on-performance-in-java

반응형