주니어 개발자 성장기
아이템 8. finalizer와 cleaner 사용을 피하라 본문
개요
자원 회수를 위해서 사용할 수 있는 finalizer와cleaner는 즉시 수행된다는 보장이 없으며 상황에 따라 위험할 수도 있어 불필요하다.
Finalizer
다음과 같이 클래스에 Object.finalize 메서드를 오버라이딩 해주면 된다.
@Override
protected void finalize() throws Throwable {
System.out.print("");
}
자바 9부터 Deprecated 되었다. 대안으로 AutoCloseable, Cleaner, WeakReference, PhantomReference 등을 제시하고 있다. (AutoCloseable가 제일 낫다고 한다)
또한 상속을 악용한 Finalizer 공격이 일어날 수도 있다.
Cleaner
다음과 같이 static class로 구현하면 된다.
// 해당 객체(this)를 내부에서 절대로 참조해선 안된다.
public static class ResourceCleaner implements Runnable {
private List<Object> resourceToClean;
public ResourceCleaner(List<Object> resourceToClean) {
this.resourceToClean = resourceToClean;
}
@Override
public void run() {
resourceToClean = null;
System.out.println("cleaned up.");
}
}
이렇게 구현한 클래스를 다음과 같이 사용하면 된다.
Cleaner cleaner = Cleaner.create();
// Cleaner의 대상이 될
List<Object> resourceToCleanUp = new ArrayList<>();
BigObject bigObject = new BigObject(resourceToCleanUp);
// Cleaner에 등록, 위에서 Runnable을 구현한 static 클래스를 2번째 매개변수로 넣어준다.
cleaner.register(bigObject, new BigObject.ResourceCleaner(resourceToCleanUp));
//bigObject의 참조가 해제되면 GC가 일어날 때 등록된 자원의 메모리가 해제된다.
권장 방법
AutoCloseable과 try-with-resources를 사용하는 것이다. Cleaner는 안전망으로서 활용할 수도 있다. (클라이언트 코드에서 실수로 try-with-resources 를 사용하지 않을 경우 GC가 수행될 기회를 줄 수 있기 때문)
AutoCloseable
AutoCloseable 인터페이스를 활용하기 위해서 AutoCloseable.close 메서드를 재정의해야 한다.close메서드는 기본 스펙에 다음과 같이 throws Exception이 정의되어 있다.
void close() throws Exception
하지만 오버라이딩 할 때 반드시 throws Exception을 해야하는 것이 아니다.
- 만약, 예외를 던져야 하는 상황이라면 throws Exception 대신에 throws {구체적인 예외 클래스}로 구현할 것을 권장한다. → 클라이언트 코드에 예외 처리 책임 전가
- 예외를 내부에서 처리할 수도 있다(try-catch 구문으로).
- 예외처리시 별다른 할 일이 필요하지 않을 때는 catch 구문에 단순히 throw new RuntimeException(e)를 추가해주는 것도 좋다.
try {
reader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
- 가급적이면
close메서드는 멱등성을 가지도록 권장한다. (클라이언트가 같은 인스턴스의close를 여러번 호출할 가능성이 있기 때문)
Finalize 공격에 대비
- 클래스를 final로 선언한다.
finalize를 미리 final로 선언해서 오버라이딩을 막는다.- 호출을 방어하고자 하는 메서드에 검증 절차를 추가한다.
@Override
final protected void finalize() throws Throwable {
super.finalize();
}
public void transfer(BigDecimal amount, String to) {
if(this.accountId.equals("푸틴"))
throw new IllegalArgumentException("푸틴은 계정을 막습니다.");
System.out.printf("transfer %f from %s to %s\n", amount, accountId, to);
}
'Java > 이펙티브 자바' 카테고리의 다른 글
| 아이템 10. equals는 일반 규약을 지켜 재정의하라 (2) | 2023.07.28 |
|---|---|
| 아이템 9. try-finally 보다 try-with-resources를 사용하라. (1) | 2023.07.23 |
| 아이템 7. 다 쓴 객체 참조를 해제하라. (1) | 2023.07.20 |
| 아이템 6. 불필요한 객체 생성을 피하라 (1) | 2023.07.17 |
| 아이템 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라. (0) | 2023.07.15 |