[Java] GC(Garbage Collector)란?

이신영·2024년 6월 18일
1

Java

목록 보기
10/12
post-thumbnail

c언어를 했을 땐 malloc()를 써서 메모리를 할당하고 free()를 꼭 아래에 써둬서 메모리를 해제해야했다. 하지만 Java에서는? new로 할당만 했지 메모리 해제를 내 손으로 해본적이 없다. 그것은 바로? GC가 해주기 때문이다!


Garbage Collector?

자바에서는 사용자에게 메모리 누수에 대해 신경쓰지 않게 하기위해서 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에 의해 주기적으로 자동 실행된다.


finalize()?

객체가 GC되기 직전에 자동으로 호출되는 메서드로 system.gc()는 가비지 컬렉션을 요청하는 용도로 사용하고, finalize()는 객체가 수거되기 전에 리소스를 정리하는 용도로 사용할 수 있다.

아니 그러면 gc()나 finalize()로 C처럼 메모리를 직접 해제해도되지않나?

두 메서드 모두 메모리를 제어할 수 있다.

System.gc()는 JVM의 최적 시점에 수행되는걸 권장해서 가급적으로 사용하지 않는걸 추천하지만?

finalize()는 오라클사에서 아예 Deprecated할 정도로 권장하지 않는다.

finalize()를 사용하면 안되는 이유

1. 비결정적 호출

finalize() 메소드는 객체가 언제 가비지 컬렉션될지 예측할 수 없기 때문에, 호출 시점을 예측할 수 없다. 이는 리소스를 적절한 타이밍에 해제해야 하는 경우 신뢰할 수 없는 방법이다.

2. 성능 저하

finalize() 메소드를 오버라이드하면 가비지 컬렉션 성능이 저하될 수 있다. finalizer(=finalize)와 관련된 추가적인 오버헤드가 발생하며, GC의 성능을 저하시킬 수 있다.

3. 안정성 문제

finalize() 메소드에서 예외가 발생하면 무시되며, 예외가 발생하면 해당 객체는 정상적으로 수거되지 않을 수 있다. 또한, finalize() 메소드에서 객체가 다시 참조되면, 객체는 다시 사용 가능한 상태가 되어 메모리 누수가 발생할 수 있다.

4. 더 나은 대안 존재

자바 9부터는 finalize() 대신 java.lang.ref.Cleaner 또는 java.lang.ref.PhantomReference를 사용하는 것이 권장된다. 이들은 더 안전하고 예측 가능한 리소스 관리를 제공한다.


profile
후회하지 않는 사람이 되자 🔥

0개의 댓글