이 질문에는 이미 답변이 있습니다.
자바에서 값 비싼 예외 던지기 및 처리 방법을 아십니까?
우리 팀에서 실제 예외 비용에 대해 여러 번 논의했습니다. 일부는 가능한 한 자주 피하고 일부는 예외를 사용하여 성능 저하가 과대 평가된다고 말합니다.
오늘 저는 소프트웨어에서 다음 코드 조각을 발견했습니다.
private void doSomething()
{
try
{
doSomethingElse();
}
catch(DidNotWorkException e)
{
log("A Message");
}
goOn();
}
private void doSomethingElse()
{
if(isSoAndSo())
{
throw new DidNotWorkException();
}
goOnAgain();
}
이것에 비해 성능은 어떻습니까?
private void doSomething()
{
doSomethingElse();
goOn();
}
private void doSomethingElse()
{
if(isSoAndSo())
{
log("A Message");
return;
}
goOnAgain();
}
코드 미학이나 다른 것에 관해서는 이야기하고 싶지 않습니다. 단지 런타임 문제 일뿐입니다! 실제 경험 / 측정이 있습니까?
나는 예외에 관해 읽는 것을 귀찮게하지 않았다. 그러나 당신의 수정 된 코드로 매우 빠른 테스트를하고 나는 예외 상황이 부울의 경우보다 상당히 느리다는 결론에 도달했다.
나는 다음과 같은 결과를 얻었다 :
Exception:20891ms
Boolean:62ms
이 코드에서 :
public class Test {
public static void main(String args[]) {
Test t = new Test();
t.testException();
t.testBoolean();
}
public void testException() {
long start = System.currentTimeMillis();
for(long i = 0; i <= 10000000L; ++i)
doSomethingException();
System.out.println("Exception:" + (System.currentTimeMillis()-start) + "ms");
}
public void testBoolean() {
long start = System.currentTimeMillis();
for(long i = 0; i <= 10000000L; ++i)
doSomething();
System.out.println("Boolean:" + (System.currentTimeMillis()-start) + "ms");
}
private void doSomethingException() {
try {
doSomethingElseException();
} catch(DidNotWorkException e) {
//Msg
}
}
private void doSomethingElseException() throws DidNotWorkException {
if(!isSoAndSo()) {
throw new DidNotWorkException();
}
}
private void doSomething() {
if(!doSomethingElse())
;//Msg
}
private boolean doSomethingElse() {
if(!isSoAndSo())
return false;
return true;
}
private boolean isSoAndSo() { return false; }
public class DidNotWorkException extends Exception {}
}
나는 어리석게도 내 코드를 충분히 읽지 못했고 이전에 버그가 있었다. (얼마나 당황 스럽다.) 누군가가이 코드를 세 번 검사 할 수 있다면 나는 그것을 매우 숙고하게 될 것이다.
내 사양은 다음과 같습니다.
내 의견으로는 예외가 아닌 메소드는 doSomethingElse에서 로그 오류를주지 않지만 대신 호출 코드가 실패를 처리 할 수 있도록 부울을 리턴한다는 것을 알아야한다. 실패 할 수있는 영역이 여러 개있는 경우 오류를 내부에 기록하거나 예외를 throw해야 할 수 있습니다.
예외는 무료가 아닙니다 ... 그래서 그들은 비쌉니다 :-)
그 책효과적인 자바이것을 상세하게 다룹니다.
필자는 예외로 인해 특정 VM 및 OS 콤보를 사용하여 자신의 컴퓨터에서 테스트 케이스를 70 배 느리게 튜닝하는 결과를 얻었습니다.
예외에 대한 느린 부분은 스택 추적을 구축하는 것입니다 (java.lang.Throwable
), 스택 깊이에 따라 다릅니다. 던지는 것은 그다지 느리지 않습니다.
예외를 사용하여 오류를 알립니다. 그러면 성능에 미치는 영향은 무시할 수 있으며 스택 추적은 실패의 원인을 정확하게 지적하는 데 도움이됩니다.
제어 흐름 (권장하지 않음)에 대한 예외가 필요하고 프로파일 링에서 병목 현상 인 예외가 표시되면 재정의하는 Exception 하위 클래스를 만듭니다fillInStackTrace()
빈 구현. 선택적으로 (또는 추가적으로) 하나의 예외를 인스턴스화하고 필드에 저장하고 항상 동일한 인스턴스를 던집니다.
다음은 마이크로 벤치 마크에 하나의 간단한 메소드를 추가하여 스택 추적이없는 예외를 보여줍니다 (그래도 결함이있다.)에서대답을 수락했다.:
public class DidNotWorkException extends Exception {
public Throwable fillInStackTrace() {
return this;
}
}
JVM을 사용하여 실행-server
모드 (Windows 7의 경우 버전 1.6.0_24)의 결과는 다음과 같습니다.
Exception:99ms
Boolean:12ms
Exception:92ms
Boolean:11ms
그 차이는 실제로 무시할만큼 작습니다.
이것은 본질적으로 JVM에 따라 다르므로, 조언이 제공된 것은 맹목적으로 신뢰하지 말고 실제로 상황을 측정해야합니다. 거친 아이디어를 얻으려면 "100 만 건의 예외를 throw하고 System.currentTimeMillis의 차이점을 인쇄하십시오"를 만드는 것이 어렵지 않습니다.
여러분이 열거 한 코드 조각을 위해 필자는 나중에 원래의 작성자에게 예외를 던지는 이유를 철저히 문서화하도록 요구할 것입니다. 나중에 "유지"하는 것이 가장 중요하지 않은 "최소한의 놀라움의 길"이 아니기 때문입니다.
(당신이 복잡한 방식으로 무언가를 할 때마다, 독자가 불필요한 작업을 왜했는지를 이해하기 위해 왜 독자가 그것을 마치 평범한 방식이 아닌 그것을 좋아 하는지를 이해하기 위해서 - 왜 그 일을 정당화해야 하는가? 그곳에는 이유가 있어야만하는 것처럼 그렇게 행해졌습니다).
예외는 매우 유용한 도구이지만 필요한 경우에만 사용해야합니다.
나는 실제 측정이 없지만 예외를 던지면 더 비쌉니다.
좋아,이 .NET 프레임 워크에 관한 링크입니다,하지만 같은 자바 적용됩니다 생각 :
즉, 당신이 그들을 사용할 때 주저해서는 안된다.적당한. 즉, 흐름 제어에 사용하지 말고 예외적 인 상황이 발생할 때 사용하십시오. 네가 일어날 것을 기대하지 않은 것.
우리가 필요로하는 곳 (예외적 인 조건)에서 예외를 사용하는 것으로 고집한다면, 당신이 지불 할 수있는 성과 위약보다 이점이 훨씬 크다고 생각합니다. 비용은 실제로 실행중인 응용 프로그램에서 예외가 발생하는 빈도의 함수이기 때문에 나는 말할 수 있습니다.
예를 들어, 실패가 예기치 않은 것이나 치명적인 결과가 아닌 것처럼 보입니다. 따라서 메소드는 예외를 사용하기보다는 성공 상태를 알리기 위해 실제로 bool을 반환해야하므로 규칙적인 제어 흐름에 포함됩니다.
필자가 참여한 성능 향상 작업에서 예외 비용은 상당히 낮습니다. 일반적으로 반복되는 작업의 복잡성을 개선하는 데 훨씬 더 많은 시간을 할애해야합니다.
모든 응답을 주셔서 감사합니다.
나는 마침내 Thorbjørn의 제안을 따랐고 성능을 직접 측정하는 작은 테스트 프로그램을 작성했습니다. 결과는 다음과 같습니다.아니두 가지 변형의 차이 (성능 문제).
코드 미학이나 무언가, 즉 예외의 의도 등을 묻지는 않았지만 대부분의 사람들이 그 주제를 다루었습니다. 그러나 현실적으로 상황이 항상 명확하지는 않습니다 ... 고려중인 상황에서 예외가 발생한 상황이 예외적 인 것으로 보이는 오래전에 코드가 생성되었습니다. 오늘날 라이브러리는 다르게 사용되며 여러 응용 프로그램의 동작과 사용이 변경되어 테스트 커버리지가 좋지 않지만 코드는 여전히 너무 느립니다 (성능에 대해 질문 한 이유입니다). 그런 상황에서 나는 A에서 B로 변화 할 좋은 이유가 있어야한다고 생각한다. 그것은 나의 생각으로는 "예외가 만들어진 것이 아니다!"가 될 수 없다는 것이다.
로깅 ( "A message")은 (다른 모든 일들에 비해)대단히비싼, 그래서 나는 이것을 제거 할 것이라고 생각한다.
편집하다:
테스트 코드는 메소드에 의해 호출 된 원래 게시물의 코드와 동일합니다.testPerfomance()
둘러싸인 루프에서System.currentTimeMillis()
실행 시간을 얻으려면 - 호출하지만 ... :
나는 지금 테스트 코드를 검토하고 다른 모든 것 (로그 문)을 돌리고 이전보다 100 배 반복하여 원래 게시물의 A 대신 B를 사용할 때 백만 호출에 대해 4.7 초를 절약한다는 것을 알게되었습니다. 론이 말했듯이fillStackTrace
가장 비싼 부분입니다 (+1). 덮어 쓰면 거의 같은 (4.5 초)를 절약 할 수 있습니다 (필요없는 경우). 결국 코드가 시간당 1000 번 호출되고 그 측정치가 그 시간에 4.5 밀리 초를 절약 할 수 있다는 것을 보여주기 때문에 내 경우에는 거의 제로 차이입니다.
그래서, 위의 첫 번째 답변 부분은 다소 오해의 소지가 있었지만 리팩터링의 비용 편익을 균형 잡기 위해 내가 말한 것은 사실입니다.
나는 당신이 약간 잘못된 각도에서 이것을 요구하고 있다고 생각합니다. 예외는 신호에 사용되도록 설계되었습니다.예외적 인 경우, 그리고프로그램 흐름 메커니즘그럴 경우. 따라서 질문해야 할 질문은 코드의 "논리"가 예외를 요구하는지 여부입니다.
예외는 일반적으로 의도 된 용도로 충분히 잘 수행되도록 설계됩니다. 그들이 병목 현상이있는 방식으로 사용된다면 무엇보다도 그 프로그램은 단지 "잘못 된 것"즉, 완전히 멈춘 것으로 나타났습니다. 즉, 근본적으로 프로그램이 무엇인지디자인문제보다는공연문제.
반대로 예외가 "올바른 작업에 사용됨"으로 나타나면 아마 OK를 수행 할 수도 있습니다.
구문 1과 2를 실행하려고 할 때 예외가 발생하지 않는다고 가정 해 봅시다. 두 샘플 코드 사이에 성능 히트가 있습니까?
그렇지 않다면DoSomething()
방법은웃었다작업량 (다른 방법에 대한 호출로드 등)?
1:
try
{
DoSomething();
}
catch (...)
{
...
}
2 :
DoSomething();