Garbage Collector 란?

Garbage Collector는 어플리케이션에 의해 사용되어지는 메모리를 관리하는 CLR의 Mechanism이다.

C언어의 경우 개발자가 메모리를 직접 할당하거나 할당을 해제할 책임이 있었다. 그러다보니 코드에 노이즈가 많고 오히려 에러가 발생할 경우가 많았다.

하지만, (적어도) C#에서는 이러한 걱정을 할 필요가 없다.

어플리케이션이 실행되면 새로운 오브젝트를 생성하기 시작하고 생성된 오브젝트들은 메모리의 두 영역(stack, healp)에 각각 저장된다.

C#의 자료형 타입에 대해서는 지난 번에도 다루었지만,

메모리의 stack 영역은 값 타입(int, bools, float, structs …)이 저장된다. 이 메모리의 stack 영역에 저장된 값들은 스레드에서 실행 처리가 완료되는 즉시 메모리에서 사라진다. (아직 배움의 깊이가 깊지 않아 사라진다는 표현 정도밖에 못하겠다 ㅎㅎ)

using System;

namespace GarbageCollector
{
	internal class Program
	{
		static void Main(string[] args)
		{
			var a = new int();
			int b = 0;

			if (args != null)
			{
				a = 5;
				b = 5;
				// 변수 a, b가 처리되는 즉시 메모리에서 사라짐
			}
			
			Console.WriteLine($"{a} / {a.GetType()}");
			Console.WriteLine($"{b} / {b.GetType()}");

		}
	}
}

그런데, Garbage Collecotr는 value type을 관리하지 않는다.

Garbage Collector는 heap 영역의 오브젝트를 관리하는데, 그래서 관리 대상은 참조 타입(strings, Lists, arrays, class에 의해 정의된 오브젝트들)이다.

using System;

namespace GarbageCollector
{
	internal class Program
	{
		public class Person
		{
			public string Name;
			public string Country;
			public BirthDay Birth;

			public Person(string name, string country, BirthDay birth)
			{
				Name = name;
				Country = country;
				Birth = birth;
			}
		}


		static void Main(string[] args)
		{
			//var a = new int();
			//int b = 0;

			//if (args != null)
			//{
			//	a = 5;
			//	b = 5;
			//	// 변수 a, b가 처리되는 즉시 메모리에서 사라짐
			//}

			//Console.WriteLine($"{a} / {a.GetType()}");
			//Console.WriteLine($"{b} / {b.GetType()}");

			var Man = new Person("이동규", "대한민국", new BirthDay(1987, 10, 16));

			if (args != null)
			{
				Man.Name = "John";
				Man.Country = "USA";
				Man.Birth = new BirthDay(1911, 5, 5);
			}

			Console.WriteLine($"His name : {Man.Name}");
			Console.WriteLine($"His country : {Man.Country}");
			Console.WriteLine($"His Birthday : {Man.Birth.Year} / {Man.Birth.Month} / {Man.Birth.Day}");
			// class에 의해 정의된 Man이 처리되면 Garbage Collector에 의해 메모리에서 사라짐 
		}
	}
}

그렇다면 Garbage Collector는 언제 메모리를 청소할까?

위에서 살펴본 바와 같이 Garbage Collector는 heap의 오브젝트를 메모리에서 바로 처리하지 않는다.

아래 케이스를 만족했을 때, 그 역할을 수행하게 된다.

  • 물리적으로 사용할 수 있는 메모리의 양이 적을 때 (OS에서 알리게 됨)
  • 힙에 할당된 객체가 사용하는 메모리의 양이 임계치를 초과했을 때, 이 임계치를 지속적으로 조절해야만 할 떄
  • G.C 메소드가 콜이 되었을 떄

Short lived한 오브젝트에 Garbage Collector를 자주 사용하는 건 성능에 되려 좋지 못할 수 있다.

Garbage Collector는 자신만의 스레드를 갖고 있지 못하기 때문에, 작동 중일 때 스레드가 멈추게 된다.

그래서 얼마동안(물론 짧은 시간이겠지만) 어플리케이션이 프리징에 걸릴 수 있다.

Garbage Collector는 메모리를 ‘조각 모음’ 할 수 있다.

예를 들어, 3개의 문자형이 메모리를 저장할 수 있는 메모리가 있다 생각해보자.

C#에서 char 형은 2byte로 구성되어있고 bit로 계산하면, 3 * 2 * 8 = 48 bit로 구성되어있다.

이 메모리에 아래와 같이 공간을 차지하는 데이터가 있다고 생각해보자.

물리적으로 사용할 수 있는 메모리의 양이 적을 때, Garbage Collector가 호출되고 저장되어야 할 데이터에 맞게 메모리 공간을 정돈하게 되는데, 이를 ‘memory defragmentation’ 조각 모음이라고 한다.

아래는 간단히 그림으로 표현한 예시이다.

image-20220706195504492

Garbage Collector는 메모리 걱정으로부터 개발자를 해방시켜주었지만, Memory leaks 현상을 100% 커버해줄 수는 없다.

지금 이직을 준비 중인 직장에서 유니티와 공동 개발한 앱이 있는데, 시스템 리소스 특히나 메모리를 너무 잡아 먹어 사양이 낮은 컴퓨터에서 펑펑 터지는 걸 봤던 지라… 이 메모리 이슈는 경험에 의해 굉장히 민감하게 반응하게 되는 것 같다.

Memory leaks는 오브젝트가 더이상 사용되지 않음에도 여전히 메모리에서 공간을 차지하고 있는 상태를 말한다.

예를 들어, 메인 윈도우에서 서브 윈도우를 실행시키기 위한 버튼을 만들었고, 이 버튼을 클릭하여 서브 윈도우를 생성하여 열었다고 생각해보자.

코드 상에서 서브 윈도우를 여는 메소드까지 작성했기 때문에 그 이후는 신경쓰지 않을 수 있다. 대게 스레드가 모든 실행을 완료하면, 메모리에서 모든 오브젝트가 사라진다고 생각하니 마련이니 말이다.

하지만, 위의 경우 스레드가 모든 실행을 완료했더라도 서브 윈도우는 메모리 상에서 사라지지 않고 공간을 차지하게 되고 이러한 문제가 누적되면서 Memory leaks 현상이 발생할 수 있다.

태그: ,

카테고리:

업데이트:

댓글남기기