관리 메뉴

클라이언트/ 서버/ 엔지니어 "게임 개발자"를 향한 매일의 공부일지

클래스와 객체 12 - 가비지 컬렉션 본문

프로그래밍 언어/자바

클래스와 객체 12 - 가비지 컬렉션

huenuri 2024. 8. 18. 08:25

가비지 컬렉션에 대한 내용은 수업 시간에 하지 않았던 내용이지만 책을 통해 학습해 보기로 했다. 분량이 많지 않아서 금방 마칠 수 있을 것 같다.


 

 

 

 

1. 객체의 소멸

자바에는 객체를 생성하는 new 연산자는 있지만 객체를 소멸시키는 연산자는 없다. 그러므로 자바에서는 개발자가 마음대로 객체를 소멸시킬 수도 없다.

객체 소멸이란 new에 의해 생성된 객체 공간을 자바 가상 기계에세 돌려주어 가용 메모리에 포함시키는 것이다. 다른 객체 지향 언어인 C++에는 delete 연산자를 두고 있으며, delete 연산자가 실행되면 객체가 곧바로 소멸된다. 그러나 자바에서는 delete 연산자도 소멸자도 없다. 할당받은 메모리를 반환해야 하는 골치 아픈 코딩 부분도 없으며 소멸자를 작성할 필요도 없다.

 

그러면 자바에서 new로 할당받은 후 사용하지 않게 된 객체 메모리는 어떻게 되는가? 이들을 가비지(garbage)라고 부르며, 자바 가상 기계의 가바지 컬렉터(garbage Collector)가 적절한 시점에 자동으로 수집하여 가용 메모리에 반환시킨다.


 

 

 

2. 가비지

가비지란 자바 응용 프로그램에서 더 이상 사용되지 않게 된 객체나 배열 메모리다. 그러면 자바 플랫폼은 가비지를 어떻게 알아내는가? 참조하는 레퍼런스가 하나도 없는 객체나 배열을 가비지로 판단한다. 왜냐하며 이 객체는 응용프로그램이 더 이상 접근할 수 없기 때문이다.

다음 코드를 실행하면 5개의 Person 객체가 생긴다.

a = new Person("이몽룡");
b = new Person("성춘향");

 

그리고 나서 다음 라인을 실행해 보자.

b = a;

 

레퍼런스 b는 a가 가리키던 객체를 가리키게 되고, b가 가리키던 처음 객체는 아무도 참조하지 않게 되어 더 이상 접근할 수 없게 되었다. 이 객체가 바로 가비지이다.

 

 


 

 

 

가비지의 발생

다음 코드에서 언제 가비지가 발생하는지 설명하라.

 

 

a는 null이 되어 문자열을 가리키는 참조가 사라진다. 이로인해 이 문자열 객체는 더 이상 참조가 되지 않아 가비지가 된다. c도 null이 되어 아무것도 참조하지 않는다. 하지만 d가 여전히 Normal을 참조하고 있으므로 c는 가비지가 아니다.

즉 a가 가비지가 되는 것이다.


 

 

 

 

3. 가비지 컬렉션(garbage collection)

가비지는 더 이상 참조되지 않기 때문에 가비지가 차지하고 있는 메모리 공간은 회수되어야 한다. 가비지가 많아지면 자바 플랫몸이 응용 프로그램에게 할당해 줄 수 있는 가용 메모리 양이 줄어들게 된다. 시간이 지날수록 자연히 가비지가 늘어나게 되며, 최악의 경우 자바 플랫폼의 가용 메모리가 이 되면 자바 응용프로그램은 더 이상 실행될 수 없게 된다.

이런 경우를 대비하여 자바 플랫폼은 가용 메모리가 일정 크기 이하로 줄어들면 자동으로 가비지를 회수하여 가용 메모리를 늘린다. 이것을 가비지 컬렉션이라고 부르며, 가바지 컬렉션은 자바 플랫폼에 의해 준비된 가비지 컬렉션 스레드(garbage collection thread)에 의해 처리된다.

 

규모가 큰 자바 프로그램은 실행 중 비교적 많은 양의 가비지를 생산한다. 그러다 가끔 가용 메모리가 부족해지는 경우가 있는데 이때 가비지 컬렉터가 실행되며 응용 프로그램은 실행을 멈추고 가비지 컬렉션이 끝나기를 기다리게 되어 사용자의 눈에는 프로그램이 중단된 것처럼 보인다. 이런 이유로 자바는 실시간 처리 응용에는 부적합한 것으로 알려져 있다.


 

 

 

4. 가비지 컬렉션 강제 요청

응용 프로그램에서 System 또는 Runtime 객체의 gc() 메서드를 호출하면 가비지 컬렉션을 요청할 수 있다. 간단히 다음 한 줄의 코드로 가능하다.

System.gc();	// 가비지 컬렉션 강제 요청

 

그러나 이 문장을 호출한 즉시 가비지 컬렉터가 작동하는 것은 아니다. 이 문장은 가비지 컬렉션이 필요하다는 요청에 불과하다. 가비지 컬랙션은 자바 플랫폼이 전적으로 판단하여 적절한 시점에 작동시킨다.


 

 

 

 

핵심 문제 풀기

 

1. 자바에서 가비지 컬렉션이 필요한 이유는 무엇인가? 가비지 컬렉션의 장단점은 무엇인가?

 

객체를 생성하는 연산자는 있지만 소멸시키는 연산자는 없어서 개발자가 마음대로 객체를 소멸시킬 수 없다. 할당받은 메모리를 돌려줌으로써 적절한 시점에 자동으로 가용 메모리에 반환시킴으로 가용 용량이 늘어난다. 장점은 프로그래머가 메모리를 관리하지 않아도 되는 편리함이 있다.  단점은 실행 중에 많은 가비지를 생성하면 응용 프로그램이 중단이 될 수도 있다는 것이다.

 

 

 

 

2. 가비지가 발생하는 것은?

1번 문제

String s1 = "가나다라";
String s2 = s1;

 

s1과 s2는 동일한 문자열 "가나다라"를 가리킨다. 여기서는 새로운 객체가 생성되지 않으며 가비지가 발생하지 않는다.


 

 

 

2번 문제

int[] a;
a = new int[10];

 

배열 a는 new int[10]으로 메모리 공간을 할당받는다. 이 경우 새로운 객체가 생성되었지만, 메모리에서 참조를 잃어버리는 동작이 없으므로 가비지는 발생하지 않는다.


 

 

 

3번 문제

String a = new String("철수");
String b = new String("영희");
String c;
c = a;
a = null;

 

String a는 철수를 참고하고, b는 영희를 참조한다. 이후 c = a;를 통해 c도 철수를 참조하게 된다. 이후 a = null로 a가 참조를 해제해도 c가 여전히 철수를 참조하고 있기 때문에 가비지가 발생하지 않는다. c가 a가 된다는 것은 c가 a의 값(참조)을 그대로 복사하여 동일한 객체를 가리킨다는 의미이다.

그러나 반대는 성립하지 않는다. a의 참조를 바꿔도 c는 여전히 철수를 가리키고 있다.


 

4번 문제

public static void main(String[] args) {
    printHello();
}

public static void printHello() {
    String hello = new String("hello!");
    System.out.println(hello);
}

 

main()에서 printHello() 메서드를 호출하면 String("Hello") 객체가 생성되지만 printHello() 메서드가 종료하고 main()으로 돌아가는 순간 String("hello") 객체는 가비지가 된다. printHello() 메서드에서 "hello!" 문자열을 참조하는 새로운 String 객체가 생성되지만 메서드 실행이 종료되면 hello 변수는 사라지고, 참조도 사라지기 때문이다.


 

 

 

3. 가비지가 발생하는지 답하기

int[] n = new int[10];
for (int i = 0; i < 10; i++) {
    Scanner s = new Scanner(System.in);
    n[i] = s.nextInt();
}

 

for 문에 의해 10번 new Scanner() 객체가 생기지만 이전에 생긴 9개의 객체들은 모두 가비지가 된다. 이전에 생성된 Scanner 객체는 더 이상 참조되지 않으므로, 매 반복 시마다 새로운 가비지가 발생하게 된다. 이 코드에서는 반복문이 10번 실행되므로, 총 9개의 Scanner 객체가 가비지로 남는 것이다.


 

 

 

 

학습을 마치고

이렇게 해서 오늘 아침에 7시 반부터 공부를 시작했는데 8시 반이 다 되어서야 이 단원을 마칠 수 있었다. 오늘 새벽에 비몽사몽한 정신으로 하다가 다 하지 못하고 취침했었다. 역시 맑은 정신으로 하니 공부가 더 잘된다. 오늘은 부모님도 오시고 예배도 드리러 가야 해서 공부는 많이 하지 못하겠지만 그래도 매일 꾸준히 하는 것만큼 더 중요한 것도 없다.