에서 finalize()
메소드가 호출되는 시기를 알아야 합니다 JVM
. finalize()
메서드를 재정 의하여 메서드를 호출 할 때 파일에 쓰는 테스트 클래스를 만들었 습니다. 실행되지 않습니다. 아무도 그것이 왜 실행되지 않는 이유를 말해 줄 수 있습니까?
finalize()
가비지 콜렉션은 영향을 미치지 않습니다.
에서 finalize()
메소드가 호출되는 시기를 알아야 합니다 JVM
. finalize()
메서드를 재정 의하여 메서드를 호출 할 때 파일에 쓰는 테스트 클래스를 만들었 습니다. 실행되지 않습니다. 아무도 그것이 왜 실행되지 않는 이유를 말해 줄 수 있습니까?
finalize()
가비지 콜렉션은 영향을 미치지 않습니다.
답변:
일반적으로 finalize()
청소 등을하지 않는 것이 가장 좋습니다 .
에 따르면 자바 독 (그것이 가치가 독서 것)이 있습니다 :
가비지 수집에서 객체에 대한 참조가 더 이상 없음을 확인할 때 객체의 가비지 수집기에 의해 호출됩니다.
Joachim이 지적했듯이, 객체에 항상 액세스 할 수 있다면 프로그램 수명 중에는 결코 일어날 수 없습니다.
또한 가비지 수집기는 특정 시간에 실행되도록 보장되지 않습니다. 일반적으로, 내가 말하려는 finalize()
것은 아마도 당신이 필요로하는 특별한 것이 없다면 일반적으로 사용하는 가장 좋은 방법은 아닐 것입니다.
finalize
유용한 상황은 무엇 입니까?
finalize()
클래스의 인스턴스 << >> 쓰레기가 아닌 경우 주요 방법이 종료되면 수집 된 기본 클래스의 메소드가 호출된다. 게다가 메인 클래스 는 응용 프로그램이 완료되기 전에 가비지 수집 될 수 있습니다. 예를 들어 "메인"스레드가 다른 스레드를 만든 다음 반환하는 다중 스레드 응용 프로그램에서. (실제로 비표준 클래스 로더가 필요합니다 ....)
finalize
객체에 대한 쓰레기 수집 얻을 때 메서드가 호출됩니다. 가비지 수집 대상이 된 후 언제든지 가능합니다.
객체는 절대 가비지 수집 finalize
되지 않으므로 호출되지 않을 수도 있습니다. 이것은 객체가 gc를 사용할 수 없게 될 때 (JVM의 전체 수명 동안 도달 할 수 있기 때문에) 또는 객체가 적합한 시간과 JVM이 실행을 중지하는 시간 사이에 실제로 가비지 콜렉션이 실행되지 않는 경우에 발생할 수 있습니다 (이는 종종 단순하게 발생 함) 테스트 프로그램).
JVM finalize
에 아직 호출되지 않은 객체 에서 실행하도록 지시하는 방법이 있지만 사용하지 않는 것이 좋습니다 (해당 메소드의 보장도 그다지 강하지는 않습니다).
finalize
응용 프로그램의 올바른 작동에 의존 하면 문제가있는 것입니다. (일반적으로 Java 이외의) 자원을 정리할 때만finalize
사용해야 합니다. 그리고 그건 정확히 JVM이되지 보장 않기 때문에 과거의 어떤 객체에 호출됩니다.finalize
Closable
인터페이스 (및 그 배후의 아이디어)는 아마도 당신이 원하는 것입니다 : .close()
리소스를 닫거나 버리고 클래스 사용자가 적시에 호출하도록 요구합니다. 당신은 할 수 추가 할 finalize
방법 "단지 저장 될"을하지만, (믿을 충분하지 않습니다 때문에) 그 실제 수정보다 더 디버깅 도구가 될 것입니다.
System.gc()
FileInputStream :: finalize ()를 호출 하도록 강제 로 호출 한 다음 파일을 이동할 수 있습니다.
protected void finalize() throws Throwable {}
- 모든 클래스는
finalize()
java.lang.Object 에서 메소드를 상속합니다.- 이 메소드는 객체에 대한 참조가 더 이상 없다고 판단되면 가비지 수집기에서 호출합니다.
- Object finalize 메서드는 동작을 수행하지 않지만 클래스에 의해 재정의 될 수 있습니다.
- 일반적으로 비 Java 자원을 정리 (예 : 파일 닫기)하도록 대체해야합니다.
재정의
finalize()
하는 경우 try-catch-finally 문을 사용하고 항상을 호출하는 것이super.finalize()
좋습니다. 이것은 클래스를 호출하는 객체가 사용하는 리소스를 실수로 놓치는 일이 없도록 안전 조치입니다.protected void finalize() throws Throwable { try { close(); // close open files } finally { super.finalize(); } }
finalize()
가비지 수집 중에 발생한 예외 는 종료를 중단하지만 그렇지 않으면 무시됩니다.finalize()
어떤 객체에서도 두 번 이상 실행되지 않습니다
인용 : http://www.janeg.ca/scjp/gc/finalize.html
이 기사를 확인할 수도 있습니다.
Effective Java, 2nd edition page 27을 확인하십시오. 항목 7 : 종료자를 피하십시오
종료자는 예측할 수없고 종종 위험하며 일반적으로 불필요합니다. 파이널 라이저에서 시간 결정적인 작업을 수행하지 마십시오. 중요한 영구 상태를 업데이트하기 위해 종료 자에 의존하지 마십시오.
리소스를 종료하려면 try-finally를 대신 사용하십시오.
// try-finally block guarantees execution of termination method Foo foo = new Foo(...); try { // Do what must be done with foo ... } finally { foo.terminate(); // Explicit termination method }
finalize()
Java 에서 메소드 가 언제 호출됩니까?
finalize 메소드는 GC가 객체에 더 이상 도달 할 수 없음을 감지 한 후 객체가 사용한 메모리를 실제로 회수하기 전에 호출됩니다.
객체에 도달 할 수없는 경우 finalize()
절대로 호출되지 않습니다.
GC가 실행되지 않으면 finalize()
호출되지 않을 수 있습니다. 일반적으로 GC는 JVM에서 가비지가 충분하다고 판단 할 때만 실행됩니다.
GC가 특정 개체에 도달 할 수없는 것으로 판단하기 전에 두 개 이상의 GC주기가 필요할 수 있습니다. Java GC는 일반적으로 "세대"수집기입니다.
GC가 객체에 도달 할 수없고 최종화 할 수 있음을 감지하면 최종 큐에 배치됩니다. 마무리는 일반적으로 일반 GC와 비동기 적으로 발생합니다.
(JVM을 사양은 실제로 수 JVM을은에 결코 .이 오브젝트에 의해 사용되는 공간을 회수하지 않는 것을 제공 ... 실행되지 파이 나라 쓸모 / 불구가 될 것이다 이런 식으로 구현 한 JVM을하지만,이 문제가 "허용"된다 .)
결말은 확정 된 시간 안에해야 할 일을 완수하기 위해 마무리에 의존하는 것이 현명하지 않다는 것입니다. 전혀 사용하지 않는 것이 "모범 사례"입니다. finalize()
메소드 에서 수행하려는 모든 작업을 수행하는 더 나은 (즉, 더 안정적인) 방법이 있어야합니다 .
마무리를 위해 합법적으로 사용하는 유일한 용도는 응용 프로그램 코드에서 손실 된 개체와 관련된 리소스를 정리하는 것입니다. 그럼에도 불구하고, 처음부터 객체를 잃지 않도록 애플리케이션 코드를 작성해야합니다. (예를 들어, Java 7+ try-with-resources 를 사용하여 close()
항상 호출되도록하십시오.)
finalize () 메서드를 재정 의하여 호출하면 파일에 쓰는 테스트 클래스를 만들었습니다. 실행되지 않습니다. 아무도 왜 그것이 실행되지 않는 이유를 말해 줄 수 있습니까?
말하기는 어렵지만 몇 가지 가능성이 있습니다.
JVM이 finalize () 메소드를 호출 할 때 불확실성이 있기 때문에 (오버라이드 된 finalize ()가 실행되는지 여부를 확실하지 않음), 연구 목적으로 finalize ()가 호출 될 때 발생하는 것을 관찰하는 더 좋은 방법은 다음과 같습니다. JVM이 명령에 의해 가비지 콜렉션을 강제로 호출하도록합니다 System.gc()
.
특히 finalize ()는 객체를 더 이상 사용하지 않을 때 호출됩니다. 그러나 새 객체를 만들어 호출하려고 할 때 확실하게 호출 할 수 없습니다. 따라서 확실히 미래에 사용되지 않는 null
객체 c
를 생성하므로 객체 c
의 최종 호출을 볼 수 있습니다.
예
class Car {
int maxspeed;
Car() {
maxspeed = 70;
}
protected void finalize() {
// Originally finalize method does nothing, but here we override finalize() saying it to print some stmt
// Calling of finalize is uncertain. Difficult to observe so we force JVM to call it by System.gc(); GarbageCollection
System.out.println("Called finalize method in class Car...");
}
}
class Bike {
int maxspeed;
Bike() {
maxspeed = 50;
}
protected void finalize() {
System.out.println("Called finalize method in class Bike...");
}
}
class Example {
public static void main(String args[]) {
Car c = new Car();
c = null; // if c weren`t null JVM wouldn't be certain it's cleared or not, null means has no future use or no longer in use hence clears it
Bike b = new Bike();
System.gc(); // should clear c, but not b
for (b.maxspeed = 1; b.maxspeed <= 70; b.maxspeed++) {
System.out.print("\t" + b.maxspeed);
if (b.maxspeed > 50) {
System.out.println("Over Speed. Pls slow down.");
}
}
}
}
산출
Called finalize method in class Car...
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51Over Speed. Pls slow down.
52Over Speed. Pls slow down.
53Over Speed. Pls slow down.
54Over Speed. Pls slow down.
55Over Speed. Pls slow down.
56Over Speed. Pls slow down.
57Over Speed. Pls slow down.
58Over Speed. Pls slow down.
59Over Speed. Pls slow down.
60Over Speed. Pls slow down.
61Over Speed. Pls slow down.
62Over Speed. Pls slow down.
63Over Speed. Pls slow down.
64Over Speed. Pls slow down.
65Over Speed. Pls slow down.
66Over Speed. Pls slow down.
67Over Speed. Pls slow down.
68Over Speed. Pls slow down.
69Over Speed. Pls slow down.
70Over Speed. Pls slow down.
주 – 최대 70까지 인쇄 한 후 프로그램에서 오브젝트 b를 사용하지 않은 후에도 "Bike 클래스의 호출 된 종료 메소드 ..."가 인쇄되지 않으므로 JVM이 b를 지 울지 여부가 불확실합니다.
System.gc();
가비지 콜렉션이 실제로 실행되는 것을 보장하는 것은 아닙니다.
finalize는 클래스 생성 횟수를 인쇄합니다.
protected void finalize() throws Throwable {
System.out.println("Run F" );
if ( checkedOut)
System.out.println("Error: Checked out");
System.out.println("Class Create Count: " + classCreate);
}
본관
while ( true) {
Book novel=new Book(true);
//System.out.println(novel.checkedOut);
//Runtime.getRuntime().runFinalization();
novel.checkIn();
new Book(true);
//System.runFinalization();
System.gc();
보시다시피 다음 출력은 클래스 수가 36 일 때 gc가 처음으로 실행되었음을 보여줍니다.
C:\javaCode\firstClass>java TerminationCondition
Run F
Error: Checked out
Class Create Count: 36
Run F
Error: Checked out
Class Create Count: 48
Run F
(테스트 중에 연결 풀을 처분하기 위해) 최근에 종료 자 방법으로 씨름을 했으므로 종료자는 많은 것이 부족하다고 말해야합니다. JavaVM 환경 (Oracle JDK, Ubuntu 15)에서 VisualVM을 사용하여 약한 참조를 사용하여 실제 상호 작용을 추적했습니다.
최종 생각
마무리 방법은 신뢰할 수 없지만 한 가지 용도로만 사용할 수 있습니다. 가비지 수집되기 전에 객체를 닫거나 폐기했는지 확인하여 수명 종료 조치와 관련된보다 복잡한 수명주기가있는 객체를 올바르게 처리 할 경우 페일 세이프를 구현할 수 있습니다. 그것이 내가 생각할 수있는 한 가지 이유는 그것을 무시하기 위해 가치가 있습니다.
라이브 스레드 또는 정적 굴절에서 도달 할 수없는 객체는 가비지 콜렉션 또는 GC에 적합합니다. 즉, 모든 참조가 널 (null) 인 경우 오브젝트가 가비지 콜렉션에 적합하다고 말할 수 있습니다. 순환 종속성은 참조로 계산되지 않으므로 오브젝트 A에 오브젝트 B에 대한 참조가 있고 오브젝트 B에 오브젝트 A에 대한 참조가 있고 다른 라이브 참조가없는 경우 오브젝트 A와 B 모두 가비지 콜렉션에 적합합니다. 일반적으로 다음과 같은 경우 객체가 Java에서 가비지 수집 대상이됩니다.
final
느린 계산 중에 객체 의 필드가 반복적으로 사용되고 그 이후에 객체가 사용되지 않으면 소스 코드가 필드를 요청할 때까지 객체가 계속 유지되거나 JIT가 필드를 임시 변수를 계산 한 다음 계산하기 전에 객체를 포기 하시겠습니까?
GC.KeepAlive()
GC가 객체를 사용할 수 있다고 가정하는 것 외에는 아무것도하지 않는 기능이 있지만 Java에서는 그러한 기능을 알지 못합니다. volatile
그 목적으로 변수를 사용할 수는 있지만 그러한 목적으로 만 변수를 사용하면 낭비가 될 것입니다.
FinalizerReference
GC주기가 필요하지 않습니다. 참조. 동기화는 발생 전 관계 를 보장하기에 충분합니다 . 마무리는 다른 스레드에서 (실제로) 실행될 수 있기 때문에 어쨌든 공식적으로 필요합니다. Java 9가 추가 될 예정입니다 Reference.reachabilityFence
.
Finalize
경우 JIT가 직접 코드를 생성 한 경우 에만 호출됩니다. 단일 스레드에서만 사용될 것으로 예상되는 객체에 대해 일반적으로 동기화 코드가 필요합니까? 객체가 버리기 전에 취소해야하는 작업을 수행하는 경우 (예 : 소켓 연결 열기 및 다른 쪽 리소스에 대한 독점 사용 획득) 코드가 여전히 소켓을 사용하는 동안 종료자가 연결을 닫으면 재앙이됩니다 . 코드가 동기화를 사용하는 것이 정상
때때로 파괴 될 때, 물체는 행동을 취해야합니다. 예를 들어, 오브젝트에 파일 핸들 또는 글꼴과 같은 비 Java 자원이있는 경우 오브젝트를 삭제하기 전에 이러한 자원이 해제되었는지 확인할 수 있습니다. 이러한 상황을 관리하기 위해 java는 "finalizing"이라는 메커니즘을 제공합니다. 이를 마무리하면 가비지 수집기에서 개체를 제거하려고 할 때 발생하는 특정 작업을 정의 할 수 있습니다. 종료자를 클래스에 추가하려면 finalize () 메소드 를 정의하십시오 . Java 실행 시간은 해당 클래스의 객체를 삭제하려고 할 때마다이 메소드를 호출합니다. finalize 메소드 내에서객체를 파괴하기 전에 수행 할 작업을 지정합니다. 가비지 수집기는 주기적으로 더 이상 실행 상태를 참조하지 않거나 참조를 가진 다른 개체를 참조하지 않는 개체를 검색합니다. 자산이 릴리스되기 전에 Java 런타임 은 오브젝트 에서 finalize () 메소드를 호출 합니다. 마무리 () 방법은 다음의 일반적인 형태를 갖는다 :
protected void finalize(){
// This is where the finalization code is entered
}
protected 키워드를 사용하면 클래스 외부의 코드 로 finalize () 에 액세스 할 수 없습니다. finalize () 는 가비지 수집 직전에 호출 된다는 것을 이해해야합니다 . 예를 들어, 개체가 범위를 벗어날 때 호출되지 않습니다. finalize () 가 언제 또는 언제 실행 될지 알 수 없음을 의미합니다 . 결과적으로, 프로그램은 시스템 자원 또는 오브젝트가 사용하는 다른 자원을 해제하기위한 다른 수단을 제공해야합니다. 프로그램을 정상적으로 실행하기 위해 finalize () 에 의존해서는 안됩니다 .
finalize 메서드를 재정의하는 클래스
public class TestClass {
public TestClass() {
System.out.println("constructor");
}
public void display() {
System.out.println("display");
}
@Override
public void finalize() {
System.out.println("destructor");
}
}
메소드가 종료 될 가능성
public class TestGarbageCollection {
public static void main(String[] args) {
while (true) {
TestClass s = new TestClass();
s.display();
System.gc();
}
}
}
메모리에 덤프 객체가 과부하되면 gc는 finalize 메소드를 호출합니다.
메모리가 과부하되면 finalize 메소드가 호출됩니다.
Java는 객체가 호출 될 수있는 finalize ()라는 메소드를 구현할 수 있도록합니다.
가비지 수집기가 개체를 수집하려고하면 finalize () 메서드가 호출됩니다.
가비지 수집기가 실행되지 않으면 메서드가 호출되지 않습니다.
가비지 수집기가 개체를 수집 하지 못하고 다시 실행하려고 하면 두 번째로 메서드가 호출되지 않습니다.
실제로는 실제 프로젝트에서 사용할 가능성이 거의 없습니다.
호출되지 않을 수도 있고 두 번 호출되지 않을 수도 있습니다. finalize () 메소드는 0 회 또는 1 회 실행될 수 있습니다.
다음 코드에서 finalize () 메서드는 가비지 수집기를 실행하기 전에 프로그램이 종료되므로 실행할 때 출력을 생성하지 않습니다.
https://wiki.sei.cmu.edu/confluence/display/java/MET12-J.+Do+not+use+finalizers 에서 지적했듯이 ,
실행 시간은 JVM (Java Virtual Machine)에 따라 다르므로 종료자를 실행해야하는 고정 시간은 없습니다. 유일한 보장은 실행되는 모든 finalizer 메소드가 연관된 오브젝트에 도달 할 수없는 후 (가비지 콜렉션의 첫 번째주기 동안 발견됨) 가비지 콜렉터가 연관된 오브젝트의 스토리지를 회수하기 전에 (가비지 컬렉터의 두 번째주기 동안) 그렇게하는 것입니다. . 객체에 도달 할 수 없게 된 후 객체의 파이널 라이저 실행이 임의로 오랜 시간 지연 될 수 있습니다. 결과적으로, 객체의 finalize () 메소드에서 파일 핸들을 닫는 것과 같이 시간이 중요한 기능을 호출하는 것은 문제가됩니다.
더 나은 이해를 위해이 프로그램을 실행 해보십시오
public class FinalizeTest
{
static {
System.out.println(Runtime.getRuntime().freeMemory());
}
public void run() {
System.out.println("run");
System.out.println(Runtime.getRuntime().freeMemory());
}
protected void finalize() throws Throwable {
System.out.println("finalize");
while(true)
break;
}
public static void main(String[] args) {
for (int i = 0 ; i < 500000 ; i++ ) {
new FinalizeTest().run();
}
}
}