Programing/Java
Effective Java - 객체 소멸
안중환
2016. 3. 4. 14:59
1_명시적 자원 해지
package kr.co.ioacademy; //iocademy 윤찬식 강사님 import java.awt.Image; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; // 핵심 : 객체 내부에서 비 메모리 자원을 사용한다면 명시적 종료 메소드를 제공해야 한다. // 이유 // 1. 가비지 컬렉션은 메모리만 수집한다. // 2. 가비지 컬렉션이 수거하지 않는 비 메모리 자원에 대해서는 // 프로그래머가 관리 해야 한다. // 3. finalize() - 종료자 // 개념 : 더 이상 참조할 수 없는 객체의 메모리 공간을 회수 할 때 GC에 의해서 호출되는 메소드 // 문제 : finalize()를 비 메모리 자원을 정리하는 용도로 사용하면 안된다. // 1. 즉시 실행된다는 보장이 없다. // 예) 종료자 안에서 파일 닫기. - JVM은 종료자를 천천히 실행하므로, // 열린 상태의 파일이 많이 남아 있을 수 있다. // 한번에 열 수 있는 파일의 개수에 제한이 있으므로 오류가 날 수 있다. // - 종료자의 실행 시점은 JVM의 구현에 의존한다. // 2. 반드시 실행된다는 보장도 없다. // - 자바 명세에는 종료자가 즉시 실행되어야 한다는 문구도 없지만, // 종료자가 반드시 실행되어야 한다는 문구도 없다. // 즉 종료자가 실행되지 않은 객체가 남은 상태로 프로그램이 종료할 수도 있다. // (동기화 객체 같은 것을 절대 종료자를 통해 반납하면 안된다) class WebPhoto { Image image; // 명시적인 종료 메소드 - OutputStream, InputStream, Socket 등 public void release() { if (image != null) { image.flush(); } } public WebPhoto(String imageUrl) { URL url; try { url = new URL(imageUrl); image = ImageIO.read(url); } catch (IOException e) { e.printStackTrace(); } } } public class Example1 { public static void main(String[] args) throws InterruptedException { // WebPhoto가 더 이상 사용되지 않는다면, 객체 내부에서 사용하고 있는 // 자원에 대해서 정리가 필요하다. WebPhoto photo = new WebPhoto("http://cfs7.tistory.com/image/14/tistory/2008/08/29/04/53/48b702410053a"); // photo = null; // photo.release(); // System.gc(); } }
2_finalize
package kr.co.ioacademy; //iocademy 윤찬식 강사님 import java.awt.Image; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; // finalize()의 용도 // : 명시적 종료 메소드 호출을 잊은 경우의 안정망을 제공할 수 있다. // 1. 그런 자원을 발견한 경우 반드시 경고 메세지를 남겨야 한다. (클라이언트 코드에 버그가 있는 것이므로) // 2. 부모의 종료자를 명시적으로 호출해야 한다. class WebPhoto { Image image; /* @Override public void finalize() { if (image != null) { System.err.println("Explicit termination method 'release' is not called"); release(); } } */ @Override public void finalize() throws Throwable { try { if (image != null) { System.err.println("Explicit termination method 'release' is not called"); release(); } } finally { super.finalize(); } } // 명시적인 종료 메소드 - OutputStream, InputStream, Socket 등 public void release() { if (image != null) { image.flush(); } } public WebPhoto(String imageUrl) { URL url; try { url = new URL(imageUrl); image = ImageIO.read(url); } catch (IOException e) { e.printStackTrace(); } } } public class Example2 { public static void main(String[] args) throws InterruptedException { WebPhoto photo = new WebPhoto("https://t1.daumcdn.net/cfile/tistory/2761F44856D7F79F2A"); // System.gc(); } }
3_finalize2
package kr.co.ioacademy; //iocademy 윤찬식 강사님 import java.awt.Image; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; // 명시적 자원 해지가 필요한 클래스는 결국 중복된 코드를 작성해야 한다. // 종료자의 역활을 일반화한 클래스 final class CloseGuard { public static CloseGuard get() { return new CloseGuard(); } private CloseGuard() {} private Throwable site; public void open(String closer) { if (closer == null) throw new NullPointerException("closer == null"); String message = "Explicit termination method '" + closer + "' not called"; site = new Throwable(message); } public void close() { site = null; } public void warnIfOpen() { if (site == null) return; System.err.println(site.toString()); } } class WebPhoto { private Image image; // Surface.java private final CloseGuard mCloseGuard = CloseGuard.get(); @Override public void finalize() throws Throwable { try { if (mCloseGuard != null) mCloseGuard.warnIfOpen(); release(); } finally { super.finalize(); } } public void release() { if (image != null) { image.flush(); } if (mCloseGuard != null) mCloseGuard.close(); } public WebPhoto(String imageUrl) { URL url; try { url = new URL(imageUrl); image = ImageIO.read(url); mCloseGuard.open("release"); } catch (IOException e) { e.printStackTrace(); } } } public class Example3 { public static void main(String[] args) { WebPhoto photo = new WebPhoto("https://t1.daumcdn.net/cfile/tistory/2761F44856D7F79F2A"); // System.gc(); } }
4_finalize3
: Finalizer Guardian Idiom(종료자 보호 패턴)
: Image를 상속 받은 클래스(MyImage)의 객체 생성 -> 상위 클래스 Image 객체도 생성 -> MyImage 객체가 GC에 의해 해지 당할 때 하 상위 클래스(Image)의 guardian이란 멤버 변수도 해지 대상이 되고, 여기서 finalize() 호출package kr.co.ioacademy; //iocademy 윤찬식 강사님 class Image { public void release() { System.out.println("Image 자원 해지"); } // @Override // protected void finalize() throws Throwable { // try { // System.out.println("Image finalize!"); // release(); // } finally { // super.finalize(); // } // } // 하위 클래스가 부모 클래스의 finalize()를 잊는 문제를 // 방지하는 방법. - (종료자 보호 패턴)Finalizer Guardian Idiom @SuppressWarnings("unused") private final Object guardian = new Object() { @Override protected void finalize() throws Throwable { release(); } }; } class MyImage extends Image { @Override protected void finalize() throws Throwable { System.out.println("MyImage finalize!"); // 잊었다.!!! // super.finalize(); } } public class Example4 { public static void main(String[] args) throws InterruptedException { MyImage image = new MyImage(); image = null; System.gc(); Thread.sleep(10000); } }