Language/JAVA

[이펙티브 자바][아이템 8] finalizer와 cleaner 사용을 피하라 - 컴도리돌이

컴도리돌이 2024. 9. 11. 09:00
728x90

이번 주제는 저에게는 매우 생소한 주제였어요 😓

finalizercleaner 같은 개념이 매우 생소하였거든요, 저는 보통 try-with-resources 구문과 @PreDestroy 어노테이션을 사용해서 자바에서 객체가 더 이상 필요하지 않을 때 리소스를 정리하곤 했는데, 객체 생성 자체를 워낙 많이 하지 않는 편이어서 더욱 생소했던 거 같네요. 그래도 열심히 책을 읽었기에, 오늘 이 주제로 글을 남기려고 합니다. 😤

 

과거에는 자바에서 객체가 더 이상 필요하지 않을 때 리소스를 정기하기 위해 finalizer를 사용했습니다. 하지만 finalizer는 다음과 같은 문제가 있었죠.

첫 번째는 finalizer는 언제 실행될지 예측할 수 없다고 합니다. 이거 매우 위험하지 않나요? 실행 시점이 정확히 알아도 사용할까 말까인데, 예측할 수 없다니,, 그래서 GC가 언제 객체를 수거할지 명확하지 않기 때문에, 리소스가 제대 해제되지 않는 문제가 발생할 수 있다고 합니다. 두 번째는 성능 저하 문제인데, finalizer의 사용은 성능을 크게 저하시킬 수 있습니다. 일반적인 객체 정리보다 최대 50배까지 느릴 수 있으며, 이는 대규모 애플리케이션에서 심각한 문제를 초래할 수 있죠. 또한 예외가 발생할 경우 예외가 무시되며 객체의 정리가 불완전하게 끝날 수 있습니다. 이는 객체를 불완전한 상태로 남겨 공격 벡터로 활용될 수 있다고 합니다. 

세 개의 문제를 알게 되었는데, 제가 왜 지금까지 몰랐는지 알 거 같네요. 실행 시점 예측 불가하고, 성능 저하 시키고, 안전성 문제도 있는 것을 굳이 협업에서 사용할 필요가 없고, 사용하면 안 될 거 같아요 😠

 

finalizer의 이러한 문제점을 해결하기 위해 자바는 cleanerAutoCloseable 인터페이스, 그리고 try-with-resources 구문을 도입했습니다. Cleanerfinalizer의 대안으로 등장했지만, 여전히 성능 저하 문제와 예측 불가능성은 여전히 남아 있어서 패스하고, AutoCloseable는 인터페이스를 구현하여 리소스가 더 이상 필요하지 않을 때 명시적으로 close() 메서드를 호출해 정리할 수 있습니다. 이는 명확한 리소스 해제를 가능하게 하고, try-with-resources와 함께 사용하면 예외 발생 시에도 안전하게 리소스를 정리할 수 있죠. 

 

스프링에서 관리되지 않는 객체나 리소스를 사용할 경우, 가장 좋은 방법은 try-with-resources를 사용하는 것이에요. 예를 들어, 파일을 읽거나 쓰는 경우 아래와 같이 작성할 수 있죠. 

try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    // 예외 처리
}

 

이 방식은 리소스 누수를 방지하고 , 코드가 간결해집니다. 스프링 부트에서도 자주 사용하는 패턴이므로 익숙해지면 큰 도움이 될 거예요😊

물론 스프링 부트를 사용하면 일반적으로 리소스 관리는 스프링 컨텍스트가 객체의 생명주기를 관리함으로써 해결돼요. 

 

 

[Spring] 스프링 빈(Bean)에 대해서 - 컴도리돌이

[Spring Boot] 스프링 제어의 역전(Inversion of Control)과 의존성 주입(Dependency Injection - DI)에 대해서 - 1. 제어의 역전(Inversion of Control) 정의 및 예제 2. 의존성 주입(Dependency Injection) 정의 및 예제 객체의

comdolidol-i.tistory.com

 

예를 들어, 데이터베이스 연결, 파일 스트림, 혹은 소켓 같은 리소스를 사용할 때 @Bean으로 등록된 객체는 컨텍스트가 종료될 때 자동으로 정리됩니다. 

@Bean
public DataSource dataSource() {
    return new HikariDataSource();  // Connection pool을 사용하는 예시
}

 

현재는 finalizercleaner의 사용을 피하고, 대신 try-with-resourcesAutoCloseable을 사용하는 것이 권장됩니다. 제가 이 두 개념에 대해 생소한 이유도, 제 앞에 길을 터주신 여러분들 덕분에 그러지 않을까요🤔 물론 특정 상황에서 cleaner를 안정망으로 사용하거나, 네이티브 리소스를 해제하는 용도로 활용할 수 있다고 하는데, 저는 지금까지 그래왔던 거처럼 이 주제는 이 글을 쓰는 것으로 마무리를 지을 거 같아요😆 


 

Effective Java - 3rd Edition Notes

Effective Java 3rd Edition Notes

ekis.github.io