finally 블록은 항상 실행됩니까?


112

마침내 자바에서 실행되지 않는 조건이 있습니까? 감사.


13
잘 알려진 특정 회사에 취직하려고 할 때이 질문을받을 수 있습니까?
Tom Hawtin-tackline

@ TomHawtin-tackline Care 이름을 지정 하시겠습니까? (하나님,이 게시물이 얼마나 오래된 말았습니다!)
Hele

6
@Hele 게임을 포기하고 싶지는 않지만 Google은 할 수 있습니다.
Tom Hawtin-tackline

짧은 대답 : 예, 정상적인 조건에서.
Shark

답변:


139

로부터 일 자습서

참고 : try 또는 catch 코드가 실행되는 동안 JVM이 종료되면 finally 블록이 실행되지 않을 수 있습니다. 마찬가지로 try 또는 catch 코드를 실행하는 스레드가 중단되거나 종료되면 응용 프로그램 전체가 계속 되더라도 finally 블록이 실행되지 않을 수 있습니다.

나는 finally 블록이 실행되지 않는 다른 방법을 모릅니다.


6
@dhiller - 나는 확신 "아래로 전원이" "JVM을 종료하면 ..."- P에 포함되어 있는지 해요
제이슨 코코

2
@Jason Coco : 종료 (전원 상실시)는 종료하는 것과 완전히 다릅니다. 후자는 전자에서 절정에 이르는 다소 조직화 된 프로세스입니다. ; p
user359996

2
AFAIK, 스레드가 중단되면 즉시 중단되지 않습니다. 인터럽트를 감지하고 작업을 중지하는 것은 스레드의 코드에 달려 있으므로 마지막으로 코드를 실행해야합니다.
Bart van Heukelom

2
finally 블록에서 예외가 발생하면 나머지 블록은 실행되지 않는다고 생각합니다.
Adriaan Koster 2011 년

그거 짜증나!
eiran

63

System.exit 는 가상 머신을 종료합니다.

현재 실행중인 Java Virtual Machine을 종료합니다. 인수는 상태 코드 역할을합니다. 일반적으로 0이 아닌 상태 코드는 비정상 종료를 나타냅니다.

이 메서드는 exit클래스 의 메서드를 호출합니다 Runtime. 이 메서드는 정상적으로 반환되지 않습니다.

    try {
        System.out.println("hello");
        System.exit(0);
    }
    finally {
        System.out.println("bye");
    } // try-finally

"bye"는 위의 코드에서 인쇄되지 않습니다.


또한 예외는 Java Virtual Machine을 종료해야합니다.
kaissun 2012-08-22

3
System.exit (0) 실행 중에 Exception이 발생하면 finally 블록이 실행됩니다.
halil

50

다른 사람들의 말을 확장하기 위해 JVM 종료와 같은 것을 유발하지 않는 것은 finally 블록을 발생시킵니다. 따라서 다음 방법 :

public static int Stupid() {
  try {
    return 0;
  }
  finally {
    return 1;
  }
}

이상하게도 컴파일하고 1을 반환합니다.


2
이것은 몇 주 전 몇 시간 동안 정말 혼란 스러웠습니다.
nickf

3
finally 블록에서 값을 반환하는 것은 나쁜 생각으로 간주됩니다. try 블록에서만 반환하거나 try / finally 블록 외부에서 반환합니다. 대부분의 IDE는이를 경고로 표시합니다.
Ran Biron

1
@nickf 나는 당신이 더 이상 혼란스럽지 않다고 생각합니다. 0이 아닌 1이 반환되는 이유에 대해 자세히 설명해 주시겠습니까? 처음에 0을 유지하는 함수의 반환 값을 저장하는 메모리 (또는 레지스터)는 finally 블록이 실행될 때 덮어 쓰여진다고 추측 할 수 있습니다. .
Yaneeve

2
C #에서는 finally 블록에서 반환 할 수 없습니다.
JMCF125

3
@RanBiron 물론입니다. 그는 실제로 finally 블록 내부에서 반환을 권장하지 않았으며 return 문조차도 해당 블록의 코드가 실행되도록 할 것이라는 점을 보여 주려고했습니다.
Aquarelle

15

System.exit와 관련하여 finally 블록이 실행되지 않는 특정 유형의 치명적인 오류도 있습니다. JVM에 메모리가 완전히 부족하면 catch 또는 최종적으로 발생하지 않고 종료 될 수 있습니다.

특히 우리가 어리석게 사용하려고 한 프로젝트가 기억납니다.

catch (OutOfMemoryError oome) {
    // do stuff
}

JVM에는 catch 블록을 실행하기위한 메모리가 남아 있지 않기 때문에 작동하지 않았습니다.


OutOfMemoryError가 발생하면 일반적으로 (GC 스 래싱을 중지하기 위해) 많은 메모리가 남아 있습니다. 그러나 반복적으로 포착하면 분명히 GC 스 래싱으로 돌아갈 것입니다.
Tom Hawtin-tackline

확인되지 않은 예외를 잡아서는 안된다고 생각했습니다!
Sergii Shevchyk

1
jdk7을 사용하여 내 편에서 시도했지만 OutOfMemory 오류를 잡았습니다!
Jaskey

10
try { for (;;); } finally { System.err.println("?"); }

이 경우 finally는 실행되지 않습니다 (비추천 항목 Thread.stop이 호출되거나 도구 인터페이스를 통해 이와 동등한 항목이 호출 되지 않는 한 ).


이 페이지는 ThreadDeath 오류가 발생하고 Thread.stop ()이 호출 될 때 스택이 정상적으로 풀린다 고 주장합니다. 내가 놓친 캐치가 있습니까? download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/guide/…
spurserh

캐치가 없다고 생각합니다. 아마도 거기에없는 캐치를 상상하고있을 것입니다. 우리가 명시 적으로 넣을 경우 throw, 다음 finally예상대로 블록이 실행됩니다. try { throw new ThreadDeath(); } finally { System.err.println("?"); }
Tom Hawtin-tackline

9

Sun 튜토리얼은이 스레드에서 잘못 인용되었습니다.

참고 : try 또는 catch 코드가 실행되는 동안 JVM이 종료되면 finally 블록 실행되지 않습니다. 마찬가지로 try 또는 catch 코드를 실행하는 스레드가 중단되거나 종료 되면 응용 프로그램이 전체적으로 계속 되더라도 finally 블록 실행되지 않습니다.

finally 블록에 대한 sun 자습서를 자세히 살펴보면 "실행하지 않을 것"이 아니라 "실행할 수 없음"이 표시됩니다. 올바른 설명은 다음과 같습니다.

참고 : try 또는 catch 코드가 실행되는 동안 JVM이 종료되면 finally 블록 실행되지 않을 수 있습니다. 마찬가지로 try 또는 catch 코드를 실행하는 스레드가 중단되거나 종료 되면 응용 프로그램 전체가 계속 되더라도 finally 블록 실행되지 않을 수 있습니다 .

이 동작의 명백한 이유는 system.exit () 호출이 런타임 시스템 스레드에서 처리되어 jvm을 종료하는 데 시간이 걸릴 수 있지만 스레드 스케줄러가 마지막으로 실행을 요청할 수 있기 때문입니다. 그래서 finally는 항상 실행되도록 설계되었지만 jvm을 종료하는 경우 최종적으로 실행되기 전에 jvm이 종료 될 수 있습니다.


6

또한 내부에서 교착 상태 / 라이브 락이 발생하는 경우 try 블록 .

이를 보여주는 코드는 다음과 같습니다.

public class DeadLocker {
    private static class SampleRunnable implements Runnable {
        private String threadId;
        private Object lock1;
        private Object lock2;

        public SampleRunnable(String threadId, Object lock1, Object lock2) {
            super();
            this.threadId = threadId;
            this.lock1 = lock1;
            this.lock2 = lock2;
        }

        @Override
        public void run() {
            try {
                synchronized (lock1) {
                    System.out.println(threadId + " inside lock1");
                    Thread.sleep(1000);
                    synchronized (lock2) {
                        System.out.println(threadId + " inside lock2");
                    }
                }
            } catch (Exception e) {
            } finally {
                System.out.println("finally");
            }
        }

    }

    public static void main(String[] args) throws Exception {
        Object ob1 = new Object();
        Object ob2 = new Object();
        Thread t1 = new Thread(new SampleRunnable("t1", ob1, ob2));
        Thread t2 = new Thread(new SampleRunnable("t2", ob2, ob1));
        t1.start();
        t2.start();
    }
}

이 코드는 다음 출력을 생성합니다.

t1 inside lock1
t2 inside lock1

"마지막으로"는 인쇄되지 않습니다.


3
기술적으로 try 블록은 절대 종료되지 않으므로 finally 블록은 실행할 기회가 없어야합니다. 무한 루프에 대해서도 마찬가지입니다.
Jeff Mercado 2012

6

try 또는 catch 코드가 실행되는 동안 JVM이 종료되면 finally 블록이 실행되지 않을 수 있습니다. ( 출처 )

정상 종료-마지막 비 데몬 스레드가 종료되거나 Runtime.exit () ( 소스 )

스레드가 종료되면 JVM은 실행중인 스레드의 인벤토리를 수행하고 남아있는 유일한 스레드가 데몬 스레드 인 경우 순서에 따라 종료를 시작합니다. JVM이 중지되면 나머지 데몬 스레드가 중단되고 최종적으로 블록이 실행되지 않고 스택이 풀리지 않고 JVM이 종료됩니다. 데몬 스레드는 드물게 사용해야합니다. 정리없이 언제든지 안전하게 버릴 수있는 처리 활동이 거의 없습니다. 특히 모든 종류의 I / O를 수행 할 수있는 작업에 데몬 스레드를 사용하는 것은 위험합니다. 데몬 스레드는 메모리 내 캐시에서 만료 된 항목을 주기적으로 제거하는 백그라운드 스레드와 같은 "하우스 키핑"작업을 위해 가장 잘 저장됩니다. ( 출처 )

마지막 비 데몬 스레드 종료 예 :

public class TestDaemon {
    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                while (true) {
                    System.out.println("Is alive");
                    Thread.sleep(10);
                    // throw new RuntimeException();
                }
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
                System.out.println("This will never be executed.");
            }
        }
    };

    public static void main(String[] args) throws InterruptedException {
        Thread daemon = new Thread(runnable);
        daemon.setDaemon(true);
        daemon.start();
        Thread.sleep(100);
        // daemon.stop();
        System.out.println("Last non-daemon thread exits.");
    }
}

산출:

Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Is alive
Last non-daemon thread exits.
Is alive
Is alive
Is alive
Is alive
Is alive

1

다음과 같은 경우 finally 블록은 실행되지 않습니다.

  • System.exit(0)에서 호출try 블록.
  • JVM 메모리가 부족한 경우
  • Java 프로세스가 작업 관리자 또는 콘솔에서 강제로 종료되는 경우
  • 귀하의 교착 상태 조건 try블록의
  • 정전으로 인해 기계가 종료되는 경우

finally 블록이 실행되지 않는 다른 프린지 케이스도있을 수 있습니다.


1

finally 블록 코드 실행을 중지하는 방법에는 두 가지가 있습니다.
1. System.exit ();
2. 어떻게 든 실행 제어가 시도 블록에 도달하지 않는 경우.
보다:

public class Main
{
  public static void main (String[]args)
  {
    if(true){
        System.out.println("will exceute");
    }else{
        try{
            System.out.println("result = "+5/0);
        }catch(ArithmeticException e){
          System.out.println("will not exceute");
        }finally{
          System.out.println("will not exceute");  
        }
    }
  }
}

0

나는 play 프레임 워크와 관련하여 실행되지 않는 finally 블록의 매우 구체적인 경우를 보았습니다.

이 컨트롤러 액션 코드의 finally 블록은 Exception 이후에만 호출되었지만 실제로 호출이 성공했을 때는 호출되지 않았다는 사실에 놀랐습니다.

try {
    InputStream is = getInputStreamMethod();
    renderBinary(is, "out.zip");
catch (Exception e) {
    e.printStackTrace();
} finally {
    cleanUp();
}

스레드가 종료되거나 renderBinary ()가 호출 될 때 무언가가 종료 될 수 있습니다. 다른 render () 호출에서도 같은 일이 발생한다고 생각하지만 확인하지는 않았습니다.

try / catch 이후로 renderBinary ()를 이동하여 문제를 해결했습니다. 추가 조사에 따르면 play는 컨트롤러 작업이 실행 된 후 실행되는 메서드를 생성하는 @Finally 주석을 제공합니다. 여기서주의 할 점은 컨트롤러에서 모든 작업을 실행 한 후에 호출되므로 항상 좋은 선택이 아닐 수 있습니다.


-1
//If ArithmeticException Occur Inner finally would not be executed
class Temp
{
    public static void main(String[] s)
    {
        try
        {
        int x = 10/s.length;
        System.out.println(x);
        try
            {
                int z[] = new int[s.length];
                z[10] = 1000;
            }catch(ArrayIndexOutOfBoundsException e)
            {
                System.out.println(e);
            }
         finally
        {
            System.out.println("Inner finally");
        }
        }
        catch(ArithmeticException e)
        {
            System.out.println(e);
        }
    finally 
    {
        System.out.println("Outer Finally"); 
    }

System.out.println("Remaining Code");   
}
}

들여 쓰기를 개선하고 세부 사항을 추가하십시오.
ROMANIA_engineer

1
실행은 내부 try 블록에 도달하지 못하며 물론 내부 finally는 실행되지 않습니다.
Anton Arhipov
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.