java에서 예외와 에러의 차이가 무엇일까요? 공식 문서는 단 2개의 키워드로 답을 주고 있습니다. abnormal, serious, shouldn't not try catch. 각각, 비정상, 심각한, catch를 하지 말아야 하는을 의미합니다.

 

 

 여기에 속한 대표적인 것들 중에서는, OOME와 stackoverflowError가 있습니다. 왜 이 둘이 예외가 아닌 에러인 건지 문득 궁금해 졌습니다.

 


 

 위 프로그램을 보면, f라는 메서드가 계속 재귀 호출이 되고 있음을 알 수 있습니다. 실제로 이 프로그램은 아래와 같은 에러를 떨굽니다.

 

 

 StackOverflowError. 이것은 재귀 함수가 종료 조건 없이 계속 타고 들어갈 때 발생할 수 있습니다.

 

 

 이 예외를 타고 들어가면, VirtualMachineError를 상속받았음을 알 수 있습니다. 이것이 무엇인지 보겠습니다.

 

 

 보면, JVM이 정상적인 상태가 아니거나, 혹은 프로그램이 수행되기 위한 리소스를 소모했을 때 발생하게 되는데요. 이러한 리소스 중에서는 스택도 포함이 되어 있습니다.

 

 

 이것은 지역 변수를 저장하기 위해서 쓰는, (스레드마다 할당이 되어 있는) 공간입니다. 그런데 그게 다 차 버린 상태에서 jvm을 계속 돌리는 것이 의미가 있을까요? 계속 스택에 쌓여가기만 하는데. 실행 시킬 이유가 없습니다. 실행을 계속 시키면 불안정 해질 겁니다. 지역 변수를 할당하려고 하는데, 허용 공간이 없어서 할당을 못 한다던지. 그러므로 catch를 해서 jvm이 불안정한 상태를 계속 이어 나가야 할 이유가 없습니다.

 

 

 그림으로 도식화를 시키면 위와 같고, StackOverflow는 VM의 자원이 부족해 지는 것이므로 VMError를 extends한 스택 오버플로우 에러를 발생시킵니다. 스택 영역이 가득 찬 상태에서 catch를 하고 더 진행해 보았자, 의미가 없기 때문입니다.

 


 OOME도 마찬가지 이유로 catch가 안 된다고 생각해 볼 수 있습니다.

 

 

 이 프로그램을 보면, 20억개의 수를 ArrayList에 넣습니다.

 

 

 그런데, 실행 결과를 보면, OOME가 떨어졌음을 알 수 있습니다.

 

 

 이는, List에 넣으려는 아이템 갯수가 너무 크기 때문입니다. 물론, 너무 크더라도, 중간에 gc가 돌아서 제거를 했다면 괜찮을 겁니다. 그런데, 이 경우에는 gc가 돌아도 할당할 수 있는 메모리가 없음을 알 수 있습니다.

 

 

 li가 도달 불가능한 객체였다면 별 문제가 되지 않았을 겁니다. 왜냐하면, 도달 불가능 하니, 가비지 처리하고 sweeping을 하면 그만이기 때문입니다. 그런데, main 메서드에 지역변수 li가 선언되었습니다. 이 문서에 의하면, Strong reachable에 대한 정의가 나옵니다. 참조 객체들을 traversing 하지 않고 some threads에서 접근 가능한 것들 이라고만 나와 있는데요.

 

 

 Main thread가 살아 있고, 이것이 li에 접근 가능한 상황입니다.

 

 

 그러면 노란색으로 칠한 부분과 군청색으로 칠한 부분은 reachable이여서, gc에 의해서 수거되지 않습니다. 이것이 계속 쌓이다 보면, JVM의 리소스가 부족해 질 겁니다. Heap space가 없는 상황에서 catch를 걸어서 계속 프로그램을 실행하는 의미가 없습니다.

 

 즉, 프로그램을 즉시 종료하거나 중단해야 하는 심각한, 비정상적인 상황이기 때문에, Error입니다. FileNotFound는 적절히 처리만 해 주면, 예를 들어 새 파일을 생성한다면, 계속 실행하게끔 할 수 있습니다. 심각한 상황은 아니고, catch가 되지 말아야 하는 상황도 아닙니다. ThreadDeath와 같은, Error를 extends 하는 또 다른 클래스들은 예외가 아닌 에러를 왜 extend 했는지 알아봐도 좋을 듯 싶습니다.