종료 메소드가 Java에 포함되는 이유는 무엇입니까?


44

이 게시물 에 따르면 , 호출 될 finalize 메소드에 의존해서는 안됩니다. Java가 왜 프로그래밍 언어에 Java를 포함 시켰는가?

호출 수 있는 함수를 프로그래밍 언어에 포함시키는 것은 끔찍한 결정 인 것 같습니다 .

답변:


41

Joshua Bloch의 Effective Java (Second Edition)에 따르면 finalize()유용한 두 가지 시나리오 가 있습니다.

  1. 하나는 객체의 소유자가 명시적인 종료 방법을 호출하는 것을 잊었을 경우 "안전망"으로 작동하는 것입니다. 종료자가 즉시 호출된다는 보장은 없지만, 클라이언트가 명시 적 종료 메소드를 호출하지 못하는 경우 (종종 드문 경우)에는 리소스를 확보하는 것이 낫습니다. 그러나 종료자는 리소스가 종료되지 않은 것으로 확인되면 경고를 기록해야합니다.

  2. 파이널 라이저의 두 번째 합법적 사용은 기본 피어가있는 객체와 관련이 있습니다. 기본 피어는 일반 개체가 기본 메서드를 통해 위임하는 기본 개체입니다. 원시 피어는 일반 오브젝트가 아니므로 가비지 콜렉터는이를 인식하지 못하며 Java 피어가 교정 될 때이를 회수 할 수 없습니다. 파이널 라이저는 기본 피어에 중요한 리소스가 없다고 가정 할 때이 작업을 수행하는 데 적합한 수단입니다. 네이티브 피어가 즉시 종료해야하는 리소스를 보유하는 경우 클래스는 위에서 설명한대로 명시적인 종료 방법을 가져야합니다. 종료 방법은 중요한 리소스를 확보하는 데 필요한 모든 작업을 수행해야합니다.

자세한 내용은 27 페이지 항목 7,를 참조하십시오.


4
네이티브 피어와 같은 # 2 소리는 안전망이 필요하고 종료 방법이 있어야하는 별도의 리소스가 아니어야하는 네이티브 리소스입니다.
Mooing Duck

2
@MooingDuck : # 2는 별도의 포인트입니다. 네이티브 피어가 중요한 리소스를 보유하지 않는 경우 (예 : 순전히 메모리 내 개체) 명시적인 종료 방법을 노출 할 필요가 없기 때문입니다. # 1의 안전망은 명시 적으로 종료 방법에 관한 것입니다.
jhominal 2016 년

55

종료자는 기본 리소스 관리에 중요합니다. 예를 들어, 객체가 Java 이외의 API를 사용하여 운영 체제에서 WidgetHandle을 할당해야 할 수 있습니다. 객체가 GC 일 때 해당 WidgetHandle을 해제하지 않으면 WidgetHandles가 유출됩니다.

중요한 것은 "파이널 라이저가 호출되지 않습니다"사례가 단순히 다음과 같이 분류된다는 것입니다.

  1. 프로그램이 빨리 종료됩니다
  2. 프로그램 수명 동안 개체는 "영원히 산다"
  3. 컴퓨터가 꺼져 / 프로세스가 OS / 기타에 의해 종료되었습니다

이 세 가지 경우 모두 (프로그램이 더 이상 실행되고 있지 않다는 이유로 인해) 기본 누수 가 없거나 이미 네이티브가 아닌 누수가 있습니다 (관리 대상 객체를 할당하지 않고 계속 할당하는 경우). GC'd).

"파이널 라이저가 호출되지 않음"경고는 실제로 프로그램 로직에 파이널 라이저를 사용하지 않는 것에 관한 것입니다. 예를 들어, 생성하는 동안 파일의 카운터를 증가시키고 종료 자에서이를 줄임으로써 프로그램의 모든 인스턴스에 존재하는 오브젝트 수를 추적하고 싶지 않습니다. 이 파일 카운터는 0으로 돌아 가지 않을 것입니다. 이것은 실제로 프로그램을 정상적으로 종료하는 데 의존하지 않아야하는 (일반적인 정전 등)보다 일반적인 원칙의 특별한 경우입니다.

그러나 네이티브 리소스를 관리하기 위해 종료자가 실행되지 않는 경우는 실행되지 않는 경우에 신경 쓰지 않는 경우에 해당합니다.


훌륭한 답변입니다. 나는 그것이 불려질지라도 같다. 그것은 여전히 ​​포함되어야한다. 질문과 StackOverflow의 링크 된 질문으로 잠시 혼란 스러웠습니다.
정보 :

3
문제가되지는 않았지만 "파이널 라이저 호출에 의존하지 마십시오"라는 맥락에서 논의 할 가치가 있습니다. 파이널 라이저가 호출 될 수 있지만 객체에 도달 할 수 없게 된 후 임의로 오랜 지연이 발생한 후에 만 ​​완료됩니다. 이것은 메모리보다 훨씬 부족한 "원시 리소스"에 중요합니다 (대부분의 리소스가 밝혀 짐).

26
"Finalizer는 네이티브 리소스 관리에 중요합니다." -죄송합니다. 죄송합니다. 네이티브 리소스를 관리하기 위해 파이널 라이저를 사용하는 것은 끔찍한 아이디어입니다. GC는 다른 리소스가 아닌 메모리에만 관심이 있습니다. 즉, 네이티브 리소스가 부족할 수 있지만 GC를 트리거 할 수있는 충분한 메모리가 남아 있습니다. 실제로는 원하지 않습니다. 또한 파이널 라이저는 GC를 더 비싸게 만드는 것도 좋지 않습니다.
Voo

12
피니셔 이외의 명시 적 네이티브 리소스 관리 전략을 유지해야하지만 객체 객체를 할당하고 파이널 라이저에서 해제하지 않으면 객체가있는 경우 네이티브 누수 상태가됩니다. 명시 적으로 리소스를 해제하지 않고 GC했습니다. 다시 말해, 종료자는 일반적으로 문제에 대한 해결책이 아니라 기본 자원 관리 전략 의 중요한 부분 입니다.
Ryan Cavanaugh

1
@Voo 아마도 객체의 계약을 따르지 않는 경우 (결제 할 close()수없는 상태가되기 전에 호출 되지 않음) 종료자가 폴백이라고 말하는 것이 더 정확할 것입니다.
ratchet freak

5

이 방법의 목적은 다음과 같이 API 설명서에 설명되어 있습니다.

Java 가상 머신이 다른 객체의 종료에 의해 취해진 액션의 결과를 제외하고, 아직 죽지 않은 임의의 스레드에 의해이 객체에 액세스 할 수있는 수단이 더 이상 없다고 판단한 경우에 불려갑니다. 또는 완성 될 준비가 된 수업 ...

일반적인 목적은 개체를 취소 할 수 없게 폐기하기 전에 정리 작업finalize수행하는 것입니다 . 예를 들어, 입출력 연결을 나타내는 객체의 finalize 메소드는 객체가 영구적으로 삭제되기 전에 명시적인 I / O 트랜잭션을 수행하여 연결을 끊을 수 있습니다.


언어 디자이너가 응용 프로그램 프로그래머가 제어 할 수없는 방식 ( "우리는 절대 신뢰할 수 없음") 을 "개체를 취소 할 수 없다"( 가비지 수집 )로 선택한 이유에 추가로 관심 이있는 경우 관련 질문대한 답변 으로 설명되었습니다 . 질문 :

자동 가비지 콜렉션 ... C 및 C ++ 프로그래머에게있어 발생하는 프로그래밍 오류의 전체 클래스를 제거합니다. 시스템에서 많은 오류를 신속하게 발견하고 프로덕션 코드가 배송 될 때까지 주요 문제가 휴면되지 않을 것이라는 확신을 가지고 Java 코드를 개발할 수 있습니다.

위의 인용문 은 Java 디자인 목표에 대한 공식 문서 에서 가져온 것입니다 . 즉, Java 언어 디자이너가 왜 이런 방식으로 결정했는지 설명하는 권위있는 참조로 간주 될 수 있습니다.

이 환경 설정에 대한 더 자세한 언어 독립적 인 논의에 대해서는 OOSC 섹션 9.6 자동 메모리 관리를 참조하십시오 (실제로이 섹션뿐만 아니라 9 장 전체에 관심이 있다면 읽을 가치가 있습니다). 이 섹션은 분명한 진술로 열립니다 :

좋은 OO 환경은 자동 메모리 관리 메커니즘을 제공하여 도달 할 수없는 객체를 감지하고 회수하여 응용 프로그램 개발자가 자신의 업무 (응용 프로그램 개발)에 집중할 수 있도록합니다.

위의 논의는 그러한 시설을 이용할 수있는 것이 얼마나 중요한지를 보여주기에 충분해야합니다. Michael Schweitzer와 Lambert Strether의 말에 따르면 :

자동 메모리 관리 기능이없는 객체 지향 프로그램은 안전 밸브가없는 압력솥과 거의 동일합니다. 조만간 폭발 할 것입니다!


4

Finalizer는 실제로 실제 상태가 아닌 경우에도 정리할 수있는 효과적인 수단이 될 것으로 예상되어 왔으며, 발명되었을 때 정리를 보장하는 더 나은 수단 (예 : 팬텀 참조 및 try-with) -resources)는 아직 존재하지 않습니다. "최종화"기능을 구현하는 데 드는 노력이 다른 정리 수단에 소비된다면 Java가 더 나을 것 같지만 Java가 처음 개발 될 때 분명하지 않았습니다.


3

주의 사항 : 나는 구식 일지 모르지만 이것은 몇 년 전의 나의 이해입니다.

일부 JVM에서는 프로그램이 종료되기 전에 완전한 GC 및 종료를 요청할 수 있지만 일반적으로 종료자가 언제 실행되는지 또는 전혀 실행되지는 않습니다. 종료하고 기본 작동 모드가 아님).

그리고 일부 GC는 벤치 마크에서 더 나은 성능을 발휘할 수 있도록 파이널 라이저가있는 GC 객체를 명시 적으로 지연 시키거나 피하는 것으로 알려져 있습니다.

불행히도 이러한 동작은 종료자가 권장 된 원래의 이유와 충돌하며 대신 명시 적으로 종료 된 종료 방법을 사용하도록 권장했습니다.

폐기하기 전에 실제로 청소해야하는 객체가 있고 사용자가 실제로이를 신뢰할 수없는 경우에도 파이널 라이저를 고려해 볼 가치가 있습니다. 그러나 일반적으로 초기 예제 중 일부에서했던 것처럼 현대 Java 코드에서 자주 보지 못하는 좋은 이유가 있습니다.


1

구글 자바 스타일 가이드는 주제에 몇 가지 현명한 조언이 있습니다 :

재정의하는 것은 매우 드 rare니다Object.finalize .

:하지 마십시오. 절대적으로 필요한 경우, 먼저 효과적인 Java Item 7, "Avoid Finalizers"를 읽고 이해 한 다음 이해하지 마십시오.


5
내가 문제를 지나치게 단순화 한 것에 감탄하는 이유는, "하지 마십시오"는 "왜 존재 하는가"에 대한 답이 아닙니다.
Pierre Arlaud

1
나는 대답에서 잘 설명하지 않았지만 (IMO) "왜 존재합니까?"에 대한 대답입니다. "하지 않아야합니다"입니다. 당신이 정말 마무리 작업을 필요로하는 그 드문 경우에, 당신은 아마 그것을 직접 구축하고자 PhantomReference하고 ReferenceQueue대신.
Daniel Pryden

비록 내가 당신을 공감하지는 않았지만 나는 분명히 *를 의미했다. 그러나 가장 선의적인 답변은 그 방법이 얼마나 유용한 지 보여 주므로, 요점을 무효화합니다.
Pierre Arlaud

1
네이티브 피어 사례에서도 PhantomReference더 나은 솔루션 이라고 생각 합니다. 파이널 라이저는 Java 초기부터 남은 사마귀이며 like Object.clone()및 raw 유형은 가장 잊혀진 언어의 일부입니다.
Daniel Pryden 2016 년

1
종료 자의 위험은 C #에서 더 접근하기 쉬운 (개발자가 이해할 수있는) 방식으로 설명됩니다. 그러나 Java와 C #의 기본 메커니즘이 동일하다는 것을 암시 해서는 안됩니다 . 사양에 그렇게 말하는 것은 없습니다.
rwong

1

Java 언어 사양 (자바 SE 7) 상태 :

Finalizer는 자동 스토리지 관리자가 자동으로 해제 할 수없는 리소스를 확보 할 수있는 기회를 제공합니다. 이러한 상황에서 객체가 사용하는 메모리를 단순히 회수한다고해서 보유한 리소스가 회수되는 것은 아닙니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.