finalize()
: JVM에 의하여 garbage collection이 수행될 때, 호출되는 함수 [depreciated]finalize()
내부에 Strong Reference를 갖도록 코딩되어 있을 경우, 제거된 객체가 부활할 수 있음Cleaner
클래스: finalize()
의 역할을 물려받음 (기능의 대체는 절대 아님, 문제점이 여전히 존재)finalize()
보다는 낫다.Cleanup functions, like finalizers, are run when an object is found to be unreachable from any class or thread. Unlike a finalizer, a cleanup function holds the state needed for cleanup separately from the object because we want the object to be reclaimed as soon as it is unreachable. The cleanup function must be able to work independently from the object. If there are any references to the object from the cleanup function it would still be reachable and could not be reclaimed. Any state needed for the cleanup must be encapsulated in the cleanup function.
finalize()
가 호출되는 시기를 예측할 수 없다.System.gc()
: 가비지 컬렉션을 보장하진 않음Runs the garbage collector. Calling the
gc
method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.
System.runFinalization()
: finalize가 수행되는 것을 보장하진 않음Runs the finalization methods of any objects pending finalization. Calling this method suggests that the Java Virtual Machine expend effort toward running the
finalize
methods of objects that have been found to be discarded but whosefinalize
methods have not yet been run. When control returns from the method call, the Java Virtual Machine has made a best effort to complete all outstanding finalizations.
그렇기 때문에, finalize(), Cleaner
를 쓴다는 것은 단순하게 안전망을 설치해주기 위한 목적에 불구하다.
(만약 자원 회수가 이루어지지 않았다고 해도, 언젠가는 해주는 것이 아예 안해주는 것 보단 낫기 때문)
finalize()
로 자원을 Release한다면 Deadlock 발생 가능FileDescriptor
로 파일을 참조한 후, finalize()
로 Release할 경우 수행 시기의 불확실성으로 인하여 외부에서 해당 FileDescriptor
값으로 외부에서 해당 파일에 접근할 수 있다. (file descriptor leak
)finalize()
와 Cleaner
는 심각한 성능 문제를 야기한다실제, AutoClosable
을 구현한 클래스를 통해 가비지 컬렉터가 수거하는 시간 보다 대략 50배 정도 느리다.
(finalize()
가 가비지 컬렉션의 효율을 떨어뜨린다)
Why do finalize() have a severe performance penalty?
finalize()
를 사용한 클래스는 finalizer
공격에 취약하다finalize()
메소드 내부에서도 자바 코드 작성 가능finalize()
를 상속받아 슈퍼 클래스의 멤버에 접근 가능생성자 및 직렬화
로직이 타겟이 된다.ex) 객체 생성 후 -> 기본 값 -> 생성자 (단순 초기화)
// 수정 될 수 없는 코드 (외부 API라고 가정)
public class Bank {
public boolean isAuthenticated() {
return false;
}
public Bank() {
if (!isAuthenticated()) {
throw new SecurityException("당신은 접근할 수 없습니다");
}
}
// target method
public void transferMoney(int money) {
System.out.println(money + "원이 이체되었습니다");
}
}
// 공격자의 코드
public class Attacker extends Bank {
@Override
protected void finalize() {
System.out.println("공격 성공");
this.transferMoney(10000);
}
}
// Driver Program
public class Main {
public static void main(String[] args) {
Bank toAttack = null;
try {
toAttack = new Attacker();
} catch (SecurityException e) {
System.out.println(e.getMessage());
}
// garbage Collector가 [new Attack()]를 수거할 때,
// 오버라이드 된 finalize()가 실행됨
System.gc();
}
}
오버라이드된 finalize()
내부에서 공격자의 코드가 실행될 수 있으므로, 객체 생성 및 여러가지 악의적인 공격이 가능해진다.
final
class : 클래스의 상속을 막는다final
finalize() : 메소드의 오버라이드를 막는다finalizer()
나 Cleaner
를 대체할 수 있는 방법인스턴스를 사용하고 난 후, 더 이상 해당 인스턴스를 사용하지 않을 때 연결된 자원들을 모두 Releasing 하고 싶은 경우
AutoCloseable
를 구현한 클래스를 설계한다.
이 때 오버라이드하는 close()
메소드는 필드에 값을 써서 자신이 닫혔는지(=유효한지) 기록하는 용도로 쓰인다
그 외의 메소드는 필드의 값을 참조하여 유효하지 않다면 Exception을 던지는 방식으로 구현한다.
// Cleaner + AutoClosable
// Cleaner 로직이 실행되는 비율이 적을수록 성능 증가, AutoClosable.close()가 많아야한다.
public class Room implements AutoClosable {
private static final Cleaner cleaner = Cleaner.create();
// Cleaning Action 정의 (Runnable)
private static class State implements Runnable {
int numJunkPiles;
State(int numJunkPiles){this.numJunkPiles = numJunkPiles;}
// Cleaner에 의해 정확히 1번만 호출된다.
@Override public void run() {
numJunkPiles = 0;
}
}
private final State state;
private final Cleaner.Cleanable cleanable;
public Room(int numJunkPiles) {
state = new State(numJunkPiles);
cleanable = cleaner.register(this, state); // cleaner를 조립 (대상 인스턴스, 행위)
}
// AutoClosable.close() 오버라이드
// close가 수행될 때, Cleaning action
@Override public void close() {
cleanable.clean();
}
}