Programming

왜 finalize ()를 구현하겠습니까?

procodes 2020. 2. 28. 19:36
반응형

왜 finalize ()를 구현하겠습니까?


나는 많은 신인 Java 질문을 읽었 finalize()으며 아무도 finalize ()가 리소스를 정리하는 신뢰할 수없는 방법이라는 것을 아무도 분명히 알지 못했다는 것을 알게되었습니다. 누군가 Connections를 정리하는 데 사용한다는 의견을 보았습니다 .Connection이 닫혔다는 보장에 가까워 질 수있는 유일한 방법은 마침내 try (catch)를 구현하는 것이므로 정말 무섭습니다.

CS에서 교육을받지는 않았지만 지금은 거의 10 년 동안 전문적으로 Java로 프로그래밍 해 왔으며 이제는 finalize()프로덕션 시스템에서 구현 하는 사람을 본 적이 없습니다. 그렇다고해서 그것이 용도가 없거나 내가 함께 일한 사람들이 제대로하고 있다는 의미는 아닙니다.

그래서 제 질문은 finalize()언어 내의 다른 프로세스 또는 구문을 통해 더 안정적으로 처리 할 수없는 구현 위해 어떤 유스 케이스가 있습니까?

Java 교과서를 반복하거나 특정 시나리오 나 경험을 제공하십시오. 그렇지 않으면이 질문의 의도가 아닌 finalize의 의도 된 용도로는 충분하지 않습니다.


외부 리소스 (소켓, 파일 등)를 보유한 객체의 백스톱으로 사용할 수 있습니다. close()호출해야하는 메소드 및 문서를 구현하십시오 .

처리가 완료되지 않은 것으로 감지 finalize()되면 close()처리를 구현 하십시오 . 아마도 stderr버그가있는 발신자를 청소하고 있음을 지적 하기 위해 무언가를 버려 두었을 수도 있습니다.

예외적이거나 번잡 한 상황에서 추가 안전을 제공합니다. 모든 발신자가 매번 올바른 일을하는 것은 아닙니다 try {} finally {}. 불행히도 대부분의 환경에서 적용됩니다.

거의 필요하지 않다는 데 동의합니다. 그리고 논평자들이 지적한 바와 같이, 그것은 GC 오버 헤드와 함께 온다. 장기 실행 앱에서 "벨트 및 멜빵"안전이 필요한 경우에만 사용하십시오.

Java 9 Object.finalize()부터는 더 이상 사용되지 않습니다. 그들은 우리를 지적 java.lang.ref.Cleaner하고 java.lang.ref.PhantomReference대안으로.


finalize()지정되지 않은 시간에 코드를 실행하는 것이 좋을 수도 있다는 JVM에 대한 힌트입니다. 이것은 코드가 신비하게 실행되지 않도록하려는 경우에 좋습니다.

종료 자에서 중요한 일 (기본적으로 로깅을 제외한 모든 것)도 세 가지 상황에서 좋습니다.

  • 다른 최종 객체는 여전히 프로그램의 나머지 부분이 유효한 것으로 간주됩니다.
  • 종료자가있는 모든 클래스의 모든 메소드에 많은 검사 코드를 추가하여 종료 후 올바르게 작동하는지 확인하려고합니다.
  • 실수로 완성 된 개체를 부활 시키려고하고, 왜 작동하지 않는지, 그리고 / 또는 결국 릴리스 될 때 마무리되지 않은 이유를 알아 내기 위해 많은 시간을 소비하려고합니다.

finalize ()가 필요하다고 생각되면 때로는 팬텀 참조가 필요합니다 (이 예제에서는 참조에 의해 사용되는 연결에 대한 하드 참조를 보유하고 팬텀 참조가 대기 한 후에 닫을 수 있습니다). 이것은 또한 신비하게도 절대로 실행되지 않을 수있는 속성을 가지고 있지만 적어도 최종 객체에서 메소드를 호출하거나 부활시킬 수는 없습니다. 따라서 연결을 완전히 닫을 필요가 없지만 상황이 좋을 때도 좋을 것입니다. 클래스의 고객은 직접 연락을 할 수 없거나 전화를 걸 수 없습니다 (실제로 공평합니다- 뭐' s 수집하기 전에 특정 조치 가 필요한 인터페이스를 설계하는 경우 가비지 콜렉터가 전혀 필요하지 않습니까? 그것은 malloc / free의 시대에 우리를 되돌려 놓는다.)

다른 경우에는 관리하려는 리소스가 더 강력해야합니다. 예를 들어, 왜 해당 연결을 닫아야합니까? 궁극적으로 시스템이 제공하는 일종의 I / O (소켓, 파일 등)를 기반으로해야하는데, 왜 가장 낮은 수준의 리소스를 확보 할 때 시스템을 닫아 시스템을 닫을 수 없습니까? 다른 쪽 끝의 서버에서 소켓을 떨어 뜨리지 않고 연결을 완전히 닫아야하는 경우 코드가 실행중인 시스템의 전원 케이블을 통해 누군가가 트립되거나 중재 네트워크가 종료되면 어떻게됩니까?

면책 조항 : 나는 과거에 JVM 구현을 위해 노력했습니다. 나는 파이널 라이저가 싫어.


간단한 규칙 : 종료자를 사용하지 마십시오.

객체가 finalizer를 가지고 있다는 사실만으로 (어떤 코드를 실행하든 상관없이) 가비지 수집에 상당한 오버 헤드가 발생하기에 충분합니다.

Brian Goetz 기사 에서 :

종료자가있는 객체 (사소한 finalize () 메서드가있는 객체)는 종료자가없는 객체와 비교하여 상당한 오버 헤드가 있으므로 드물게 사용해야합니다. 최종화 가능 객체는 할당 속도가 느리고 수집 속도가 느립니다. 할당시 JVM은 완료 가능 오브젝트를 가비지 콜렉터에 등록해야하며 (최소한 HotSpot JVM 구현에서) 완료 가능 오브젝트는 대부분의 다른 오브젝트보다 느린 할당 경로를 따라야합니다. 마찬가지로, 최종화 가능한 객체도 수집 속도가 느립니다. 종료 가능한 객체를 회수하려면 가비지 수집주기가 2 회 이상 (가장 좋은 경우) 필요하며 가비지 수집기는 마무리 작업을 호출하기 위해 추가 작업을 수행해야합니다. 결과적으로 객체를 할당하고 수집하는 데 더 많은 시간을 소비하고 가비지 수집기에 더 많은 압력을 도달 할 수없는 최종 개체에 사용되는 메모리가 더 오래 유지되기 때문입니다. 이를 파이널 라이저가 예측 가능한 기간 또는 전혀 실행하지 않을 수 있다는 사실과 결합하면 최종화가 올바른 도구로 사용되는 상황이 상대적으로 적다는 것을 알 수 있습니다.


프로덕션 코드에서 마무리 작업을 사용한 유일한 시간은 주어진 객체의 리소스가 정리되었는지 확인하고 그렇지 않은 경우 매우 보컬 메시지를 기록하는 것입니다. 실제로 시도하지 않았고 제대로 수행되지 않으면 크게 외쳤습니다. 매우 유용한 것으로 판명되었습니다.


1998 년부터 전문적으로 Java를 해왔으며 구현 한 적이 없습니다 finalize(). 한 번도 아닙니다.


받아 들여진 대답은 훌륭합니다. 단지 실제로 사용하지 않고 마무리 기능을 가질 수있는 방법이 있다고 덧붙였습니다.

"참조"클래스를보십시오. 약한 참조, 팬텀 참조 및 소프트 참조.

그것들을 사용하여 모든 객체에 대한 참조를 유지할 수는 있지만이 참조는 GC를 중지시키지 않습니다. 이것에 대한 깔끔한 점은 삭제 될 때 메소드를 호출하도록 할 수 있으며이 메소드 가 호출 되도록 보장 할 수 있다는 것입니다.

마무리에 관해서 : 어떤 객체가 해제되었는지 이해하기 위해 finalize를 한 번 사용했습니다. 스태틱, 레퍼런스 카운팅 등을 사용하여 깔끔한 게임을 할 수는 있지만 분석만을위한 것이지만 이와 같은 코드를주의 깊게 살펴보십시오.

public void finalize() {
  ref1 = null;
  ref2 = null;
  othercrap = null;
}

누군가 자신이하는 일을 알지 못했다는 신호입니다. 이와 같은 "정리"는 거의 필요하지 않습니다. 수업이 GC되면 자동으로 이루어집니다.

마무리에서 이와 같은 코드를 찾으면 코드를 작성한 사람이 혼란스러워 할 것입니다.

다른 곳이라면 코드가 잘못된 모델에 대한 유효한 패치 일 수 있습니다 (클래스가 오랫동안 유지되며 어떤 이유로 든 객체가 GC되기 전에 수동으로 해제해야했습니다). 일반적으로 누군가 리스너 또는 무언가를 제거하는 것을 잊어 버렸고 왜 객체가 GC가 아닌지 알아낼 수 없으므로 참조하는 것을 삭제하고 어깨를 으 and하고 멀리 걸어갑니다.

"Quicker"를 정리하는 데 사용해서는 안됩니다.


이걸로 무엇을 만들 수 있는지 잘 모르겠지만 ...

itsadok@laptop ~/jdk1.6.0_02/src/
$ find . -name "*.java" | xargs grep "void finalize()" | wc -l
41

내가 추측 그래서 태양은 발견 일부 가 사용되어야한다 (그들이 생각하는) 경우를.


class MyObject {
    Test main;

    public MyObject(Test t) {    
        main = t; 
    }

    protected void finalize() {
        main.ref = this; // let instance become reachable again
        System.out.println("This is finalize"); //test finalize run only once
    }
}

class Test {
    MyObject ref;

    public static void main(String[] args) {
        Test test = new Test();
        test.ref = new MyObject(test);
        test.ref = null; //MyObject become unreachable,finalize will be invoked
        System.gc(); 
        if (test.ref != null) System.out.println("MyObject still alive!");  
    }
}

=====================================

결과:

This is finalize

MyObject still alive!

======================================

따라서 finalize 방법으로 연결할 수없는 인스턴스에 도달 할 수 있습니다.


finalize()리소스 누수를 포착하는 데 유용 할 수 있습니다. 리소스를 닫아야하지만 로그 파일로 닫히지 않았다는 사실을 기록하지 않은 경우 리소스를 닫습니다. 이렇게하면 리소스 누수를 제거하고 문제가 발생했음을 알 수 있으므로 해결할 수 있습니다.

1.0 alpha 3 (1995) 이후 Java로 프로그래밍 해 왔으며 아직 마무리 작업을 재정의하지 않았습니다 ...


리소스를 정리하기 위해 finalize ()에 의존해서는 안됩니다. finalize ()는 클래스가 가비지 수집 될 때까지 실행되지 않습니다. 리소스 사용이 끝나면 명시 적으로 리소스를 비우는 것이 훨씬 좋습니다.


위의 답변에서 요점을 강조하기 위해 : 파이널 라이저는 외로운 GC 스레드에서 실행됩니다. 필자는 개발자가 일부 파이널 라이저에 약간의 절전 모드를 추가하고 의도적으로 멋진 3D 데모를 무릎에 넣은 주요 Sun 데모에 대해 들었습니다.

테스트 환경 진단을 제외하고는 피하는 것이 가장 좋습니다.

Eckel 's Thinking in Java에는 이것에 대한 좋은 섹션 이 있습니다.


흠, 기존 풀로 반환되지 않은 개체를 정리하는 데 사용한 적이 있습니다.

그들은 많이 지나갔으므로 언제 수영장으로 안전하게 돌아갈 수 있는지 알 수 없었습니다. 문제는 가비지 수집 중에 개체 풀링으로 인한 절감보다 훨씬 큰 페널티가 발생한다는 것입니다. 전체 풀을 뜯어 내고 모든 것을 역동적으로 만들고 완성하기 전에 약 한 달 동안 프로덕션에있었습니다.


에서하는 일에주의하십시오 finalize(). 특히 자원을 정리하기 위해 close () 호출과 같은 용도로 사용하는 경우. 우리는 실행중인 Java 코드에 JNI 라이브러리가 연결되어있는 여러 상황을 겪었고 finalize ()를 사용하여 JNI 메소드를 호출하는 모든 상황에서 Java 힙 손상이 매우 심해졌습니다. 기본 JNI 코드 자체로 인해 손상이 발생한 것은 아니며 기본 라이브러리에서 모든 메모리 추적에 문제가 없었습니다. 우리가 finalize ()에서 JNI 메소드를 호출한다는 것은 단지 사실이었습니다.

이것은 여전히 ​​널리 사용되는 JDK 1.5에서 이루어졌습니다.

우리는 나중에 무언가 잘못되었다는 것을 알지 못했지만, 결국 범인은 항상 JNI 호출을 사용하는 finalize () 메소드였습니다.


자원을 확보하기 위해 일종의 "정리"메소드를 호출해야하는 다른 개발자가 사용할 코드를 작성할 때. 때로는 다른 개발자가 정리 (또는 닫기, 파괴 또는 기타) 메소드 호출을 잊어 버리는 경우가 있습니다. 가능한 리소스 누수를 피하기 위해 finalize 메서드를 확인하여 메서드가 호출되었는지 확인하고 그렇지 않은 경우 직접 호출 할 수 있습니다.

많은 데이터베이스 드라이버는 Statement 및 Connection 구현에서이 작업을 수행하여 호출을 잊어 버린 개발자에 대해 약간의 안전을 제공합니다.


편집 : 좋아, 그것은 실제로 작동하지 않습니다. 나는 그것을 구현하고 때로는 실패하면 괜찮지 만 생각했지만 finalize 메소드를 한 번만 호출하지도 않았다.

나는 전문 프로그래머는 아니지만 내 프로그램에서는 finalize ()를 사용하는 좋은 경우의 예라고 생각하는 경우가 있습니다. 즉, 내용이 파괴되기 전에 디스크에 내용을 쓰는 캐시입니다. 매번 파괴 될 때마다 실행될 필요는 없으므로 프로그램 속도가 빨라지기 때문에 잘못하지 않았기를 바랍니다.

@Override
public void finalize()
{
    try {saveCache();} catch (Exception e)  {e.printStackTrace();}
}

public void saveCache() throws FileNotFoundException, IOException
{
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp"));
    out.writeObject(cache);
}

전역 / 정적 장소에 추가 된 (필요하지 않은) 항목을 제거하는 것이 편리하며 개체를 제거 할 때 제거해야합니다. 예를 들어 :

    private void addGlobalClickListener () {
        weakAwtEventListener = 새로운 WeakAWTEventListener (this);

        Toolkit.getDefaultToolkit (). addAWTEventListener (weakAwtEventListener, AWTEvent.MOUSE_EVENT_MASK);
    }

    @우세하다
    protected void finalize ()에서 Throwable {
        super.finalize ();

        if (weakAwtEventListener! = null) {
            Toolkit.getDefaultToolkit (). removeAWTEventListener (weakAwtEventListener);
        }
    }

iirc-고가의 리소스에 대한 풀링 메커니즘을 구현하는 수단으로 finalize 메서드를 사용할 수 있으므로 GC도 얻지 못합니다.


참고로 :

finalize ()를 재정의하는 객체는 가비지 수집기에서 특별히 처리합니다. 일반적으로 개체가 더 이상 범위 내에 있지 않으면 수집주기 동안 개체가 즉시 소멸됩니다. 그러나 최종화 가능한 객체는 대신 대기열로 이동합니다. 여기서 별도의 마무리 스레드가 대기열을 비우고 각 객체에서 finalize () 메서드를 실행합니다. finalize () 메소드가 종료되면 다음 사이클에서 객체는 가비지 수집 준비가됩니다.

출처 : finalize ()는 Java-9에서 더 이상 사용되지 않습니다.


리소스 (파일, 소켓, 스트림 등)는 일단 완료되면 닫아야합니다. 그들은 일반적으로 close()우리가 일반적으로 진술 finally섹션 에서 호출하는 방법을 가지고 있습니다 try-catch. finalize()최종 개발자가 항상 호출 될 것이라는 보장이 없기 때문에 가끔은 소수의 개발자도 사용할 수 있지만 IMO는 적합하지 않습니다.

Java 7에서는 다음 과 같이 사용할 수있는 try-with-resources 문이 있습니다.

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  // Processing and other logic here.
} catch (Exception e) {
  // log exception
} finally {
  // Just in case we need to do some stuff here.
}

위의 예에서 try-with-resource는 메소드 BufferedReader를 호출 하여 자원 자동으로 닫습니다 close(). 원하는 경우 자체 클래스에서 Closeable구현 하고 비슷한 방식으로 사용할 수 있습니다. IMO 더 깔끔하고 이해하기 쉬운 것 같습니다.


개인적으로, 나는 finalize()한 가지 희귀 한 상황을 제외하고는 거의 사용하지 않았습니다 . 사용자 정의 제네릭 형식 컬렉션을 만들고 finalize()다음을 수행 하는 사용자 정의 방법을 작성했습니다 .

public void finalize() throws Throwable {
    super.finalize();
    if (destructiveFinalize) {
        T item;
        for (int i = 0, l = length(); i < l; i++) {
            item = get(i);
            if (item == null) {
                continue;
            }
            if (item instanceof Window) {
                ((Window) get(i)).dispose();
            }
            if (item instanceof CompleteObject) {
                ((CompleteObject) get(i)).finalize();
            }
            set(i, null);
        }
    }
}

( CompleteObject당신은 당신이 거의 구현하지 구현 한 것을 지정할 수 있습니다 내가 만든 인터페이스입니다 Object방법처럼은 #finalize(), #hashCode()#clone())

따라서 자매 #setDestructivelyFinalizes(boolean)방법을 사용하면 내 컬렉션을 사용하는 프로그램은이 컬렉션에 대한 참조를 삭제하면 내용에 대한 참조도 삭제되고 JVM을 의도하지 않게 유지하는 모든 창을 폐기 할 수 있습니다. 나는 또한 스레드를 멈추는 것을 고려했지만 완전히 새로운 웜 캔을 열었습니다.


승인 된 답변은 마무리하는 동안 리소스를 닫을 수 있음을 나타냅니다.

그러나이 답변 은 적어도 JIT 컴파일러가있는 java8에서는 예기치 않은 문제가 발생하여 객체가 유지 관리하는 스트림에서 읽기를 마치기 전에도 최종자가 호출되는 경우가 있습니다.

따라서 그러한 상황에서도 finalize를 호출하는 것은 권장 되지 않습니다 .

참고 URL : https://stackoverflow.com/questions/158174/why-would-you-ever-implement-finalize



반응형