c언어를 했을 땐 malloc()
를 써서 메모리를 할당하고 free()
를 꼭 아래에 써둬서 메모리를 해제해야했다. 하지만 Java에서는? new
로 할당만 했지 메모리 해제를 내 손으로 해본적이 없다. 그것은 바로? GC가 해주기 때문이다!
자바에서는 사용자에게 메모리 누수에 대해 신경쓰지 않게 하기위해서 GC를 제공한다.
런타임때 메모리는 주로 스택과 힙 영역을 사용하는데 스택은 메서드 호출 시 지역 변수들을 저장하는 데 사용되고, 힙은 동적으로 생성된 객체들이 저장된다. 스택은 메서드가 끝나면 알아서 해제되지만 힙 영역의 메모리는 보통 동적으로 생성되는데 해제하는 것은 누가 할까?
바로 GC가 그 역할을 담당한다.
자바의GC는 힙 영역에 동적으로 할당되었으나 더 이상 참조되지 않는 대상을 탐지하여 메모리에서 해제하는 JVM의 기능이다. 이로 인해 개발자는 메모리 누수에 대해 신경 쓰지 않아도 된다. 가비지 컬렉션은 주기적으로 힙 영역을 스캔하여 참조되지 않는 객체를 자동으로 정리함으로써 메모리 효율성을 유지해준다.
public class GCDemo {
static class MyObject {
private int id;
MyObject(int id) {
this.id = id;
System.out.println("MyObject " + id + " created");
}
@Override
protected void finalize() throws Throwable {
System.out.println("MyObject " + id + " is finalized");
super.finalize();
}
}
public static void main(String[] args) {
MyObject obj1 = new MyObject(1);
MyObject obj2 = new MyObject(2);
// 객체 참조 해제
obj1 = null;
obj2 = null;
// 가비지 컬렉션 요청
// 근데 이건 평상시엔 안쓰는게 좋음
System.gc();
// 잠시 대기하여 가비지 컬렉션이 발생할 시간을 줌
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//출력결과
MyObject 1 created
MyObject 2 created
MyObject 1 is finalized
MyObject 2 is finalized
예시 코드가 있다. 이 코드에서 MyObject
클래스의 객체를 할당하고 null로 참조를 해제하였다.
그럼 null 선언하면 gc가 가져감?
그건 아니고 참조를 해제하였으니 gc를 호출할 수 있는 조건을 달성하였다고 이해하면된다. 코드에서처럼 gc를 명시하는 경우는 거의 없고 JVM에 의해 주기적으로 자동 실행된다.
객체가 GC되기 직전에 자동으로 호출되는 메서드로 system.gc()
는 가비지 컬렉션을 요청하는 용도로 사용하고, finalize()
는 객체가 수거되기 전에 리소스를 정리하는 용도로 사용할 수 있다.
두 메서드 모두 메모리를 제어할 수 있다.
System.gc()는 JVM의 최적 시점에 수행되는걸 권장해서 가급적으로 사용하지 않는걸 추천하지만?
finalize()는 오라클사에서 아예 Deprecated할 정도로 권장하지 않는다.
finalize() 메소드는 객체가 언제 가비지 컬렉션될지 예측할 수 없기 때문에, 호출 시점을 예측할 수 없다. 이는 리소스를 적절한 타이밍에 해제해야 하는 경우 신뢰할 수 없는 방법이다.
finalize() 메소드를 오버라이드하면 가비지 컬렉션 성능이 저하될 수 있다. finalizer
(=finalize)와 관련된 추가적인 오버헤드가 발생하며, GC의 성능을 저하시킬 수 있다.
finalize() 메소드에서 예외가 발생하면 무시되며, 예외가 발생하면 해당 객체는 정상적으로 수거되지 않을 수 있다. 또한, finalize() 메소드에서 객체가 다시 참조되면, 객체는 다시 사용 가능한 상태가 되어 메모리 누수가 발생할 수 있다.
자바 9부터는 finalize() 대신 java.lang.ref.Cleaner
또는 java.lang.ref.PhantomReference
를 사용하는 것이 권장된다. 이들은 더 안전하고 예측 가능한 리소스 관리를 제공한다.