프로그래밍 언어에서 메모리 관리를 자동화하는 기능. 개발자가 명시적으로 메모리를 해제하지 않아도, 더 이상 명시적으로 메모리를 해제하지 않아도, 더 이상 필요하지 않은 메모리를 자동으로 식별하고 회수하여 메모리 누수를 방지하고 프로그램 성능을 유지하는 역할을 합니다.
이를 자동으로 하여 개발자의 부담을 줄이고 오류를 방지하기 위해 GC가 도입
public class Main {
public static void main(String[] args) {
String a = new String("Hello"); // 객체 "Hello" 생성
String b = a; // b가 a를 참조
a = null; // a가 참조를 해제
// 이제 "Hello" 객체는 b에 의해 여전히 참조됨.
b = null; // b도 참조를 해제
// "Hello" 객체는 더 이상 참조되지 않음 -> Unreachable 상태
}
}
힙 영역에서 동작하며 객체가 더 이상 사용되지 않으면 해당 메모리를 회수
: 힙 영역이란 프로그램 실행 중에 동적으로 생성되는 객체들이 저장되는 메모리 공간으로 보통 참조 변수를 통해 접근할 수 있습니다.(Java - new키워드)
<-> 스택 메모리는 함수 호출과 함께 할당 및 해제가 즉시 이루어지기 때문에 GC의 대상이 아니다.
가비지 컬렉션의 동작 단계
1. Mark - 모든 객체를 탐색하면서, 활성 상태(참조되고 있는 객체)인지 확인합니다.
2. Sweep - 활성 상태가 아닌 객체를 해제하여 메모리를 회수합니다.
3. Compaction(이 단꼐는 선택적이다) - 메모리를 정리하고 조각(Fragmentation)을 최소화하고 연속적인 빈 공간을 확보합니다.
GC의 주요 알고리즘
1. 참조 카운팅(Reference Counting) -> 파이썬과 java의 차이점 참고
2. 추적 기반(Tracing)
public class Main {
public static void main(String[] args) {
Node a = new Node("A");
Node b = new Node("B");
Node c = new Node("C");
a.next = b; // A -> B
b.next = c; // B -> C
c.next = null; // C -> null (끝)
}
}
위 코드에서 각 객체 A, B, C는 서로 연결된 참조 그래프를 형성
: 추적 기반 방식에서는 Root 객체에서 시작하여 객체 그래프를 탐색합니다. Root 객체는 현재 프로그램에서 직접적으로 참조되는 객체를 말합니다. ex) 전역 변수, 스택에 저장된 지역 변수, 정적 필드 등이 Root가 됩니다.
-> Mark: Root 객체에서 시작하여 모든 참조를 따라가면서 Reachable 객체(도달 가능한 객체)를 마킹합니다. 도달하지 못한 객체는 자동으로 Unreachable로 간주됩니다. -> Sweep: 마킹되지 않은(Unreachable) 객체를 메모리에서 제거합니다. -> Compaction(선택적): 힙 메모리를 재정렬하여 메모리 단편화(Fragmentation)을 줄이고 연속적인 공간을 확보합니다.
Java에서의
Stop-the-World는 GC가 실행되는 동안 애플리케이션이 멈추는 방식입니다.
즉 가비지 컬렉션의 장단점은
메모리 관리의 자동화로 프로그래머의 부담이 줄어들고 메모리 누수 및 잘못된 해제 오류를 방지할 수 있기 때문에 안정적인 애플리케이션 실행이 가능하다.
하지만 GC 수행 중 Stop-the-World 현상등 애플리케이션 성능이 저하될 수 있고 실시간 응답성이 중요한 프로그램에서는 이로 인해 비효율적일 수 있다. 또한 직접적으로 해제를 할 수 없기에 비효율적인 메모리 해제가 발생할 수 있다.
GC가 발생하는 시점은 JVM이나 런타임 환경에 따라 다르지만 보통
1. 메모리가 부족할 때
2. 프로그램이 여유 상태일 때
3. 명시적으로 System.gc() 호출될 때 일어난다.
효율적인 GC를 위해서는 불필요한 객체 생성을 피해야 하며 사용하지 않는 객체를 명시적으로 null로 설정하는 참조 해제(Nullify)를 사용하고 객체의 생명주기를 고려한 설계를 해야한다. 또한 GC 최적화를 위해 JVM 옵션을 조정할 수 있다.
신입 백엔드 개발자로서 취업 준비 상황에 맞춰, 가비지 컬렉션(GC)과 관련된 실습 중 꼭 필요한 것들만 간추려서 정리해드릴게요!
GC는 백엔드 시스템의 성능에 영향을 주기 때문에, 기본적인 GC 로그 분석 능력은 신입 개발자에게 큰 강점이 될 수 있습니다.
간단한 Java 프로젝트를 실행하고, GC 로그를 활성화해 동작을 관찰.
java -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -jar your-app.jar
로그에서 주요 항목 확인:
도구를 사용해 분석:
GC 알고리즘을 이해하면 프로젝트 환경에 맞는 JVM 설정을 제안하거나, 튜닝 방향을 제시할 수 있습니다.
-XX:+UseSerialGC-XX:+UseParallelGC-XX:+UseG1GC메모리 누수는 백엔드 시스템에서 종종 발생하는 문제입니다. 이를 해결하는 능력은 면접에서 차별화된 강점이 됩니다.
의도적인 메모리 누수 코드 작성:
import java.util.ArrayList;
import java.util.List;
public class MemoryLeakDemo {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
while (true) {
list.add(new Object()); // 메모리를 계속 소비
}
}
}
Heap Dump 생성:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./dump.hprofEclipse MAT로 힙 덤프 분석:
면접에서 "GC를 어떻게 튜닝해봤나요?" 같은 질문에 답할 준비를 할 수 있습니다.
-Xms512m -Xmx2g -XX:+UseG1GC.이 정도만 진행하면 신입 개발자로서 GC에 대한 기본 실무 역량을 충분히 갖출 수 있습니다. 궁금한 점이 있거나 더 필요한 자료가 있다면 말씀해주세요! 😊