다음을 통해 공유


자동 메모리 관리

자동 메모리 관리는 관리되는 실행 중에 공용 언어 런타임이 제공하는 서비스 중 하나입니다. 공용 언어 런타임의 가비지 수집기는 애플리케이션에 대한 메모리 할당 및 릴리스를 관리합니다. 개발자의 경우 관리되는 애플리케이션을 개발할 때 메모리 관리 작업을 수행하기 위해 코드를 작성할 필요가 없습니다. 자동 메모리 관리는 개체 해제를 잊고 메모리 누수를 유발하거나 이미 해제된 개체의 메모리에 액세스하는 것과 같은 일반적인 문제를 제거할 수 있습니다. 이 섹션에서는 가비지 수집기가 메모리를 할당하고 해제하는 방법을 설명합니다.

메모리 할당

사용자가 새 프로세스를 시작하면 런타임에서는 인접한 주소 공간 영역을 이 프로세스에 예약합니다. 이 예약된 주소 공간을 관리되는 힙이라고 합니다. 관리되는 힙에서는 힙에 있는 다음 개체가 할당될 주소의 포인터를 관리합니다. 초기에 이 포인터는 관리되는 힙의 기본 주소로 설정됩니다. 모든 참조 형식 은 관리되는 힙에 할당됩니다. 애플리케이션에서 참조 형식을 처음으로 만드는 경우, 이 참조 형식에 대한 메모리는 관리되는 힙의 기본 주소로 할당됩니다. 애플리케이션이 다음 개체를 만들 때 가비지 수집기는 첫 번째 개체 바로 다음 주소 공간에 메모리를 할당합니다. 주소 공간을 사용할 수 있는 한 가비지 수집기는 이러한 방식으로 새 개체에 대한 공간을 계속 할당합니다.

관리되는 힙에서 메모리를 할당하면 관리되지 않는 힙에서 메모리를 할당하는 것보다 속도가 더 빠릅니다. 런타임은 포인터에 값을 추가하여 개체에 대한 메모리를 할당하므로 스택에서 메모리를 할당하는 속도만큼 빠릅니다. 또한 연속적으로 할당되는 새 개체는 관리되는 힙에 연속적으로 저장되므로 애플리케이션은 개체에 매우 빠르게 액세스할 수 있습니다.

메모리 해제

가비지 수집기의 최적화 엔진은 수행 중인 할당에 따라 수집을 수행하기에 가장 적합한 시간을 결정합니다. 수집을 수행할 때 가비지 수집기는 애플리케이션에서 더 이상 사용되지 않는 개체에 대한 메모리를 해제합니다. 애플리케이션의 루트를 검사하여 더 이상 사용되지 않는 개체를 결정합니다. 모든 애플리케이션에는 루트 집합이 있습니다. 각 루트는 관리되는 힙에 있는 개체를 참조하거나 Null로 설정됩니다. 애플리케이션의 루트에는 정적 필드, 스레드 스택의 지역 변수 및 매개 변수 및 CPU 레지스터가 포함됩니다. 가비지 수집기는 JIT(Just-In-Time) 컴파일러 와 런타임이 유지 관리하는 활성 루트 목록에 액세스할 수 있습니다. 이 목록을 사용하여 애플리케이션의 루트를 검사하고, 프로세스에서 루트에서 연결할 수 있는 모든 개체가 포함된 그래프를 만듭니다.

그래프에 없는 개체는 애플리케이션의 루트에서 연결할 수 없습니다. 가비지 수집기는 연결할 수 없는 개체를 가비지로 간주하고 할당된 메모리를 해제합니다. 수집을 수행할 때 가비지 수집기는 연결할 수 없는 개체에서 사용되는 주소 공간 블록을 찾기 위해 관리되는 힙을 검사합니다. 연결할 수 없는 개체가 발견되면 가비지 수집기는 메모리 복사 기능을 사용하여 메모리에서 연결할 수 있는 개체를 압축합니다. 그러면 연결할 수 없는 개체에 할당된 주소 공간 블록이 해제됩니다. 연결할 수 있는 개체의 메모리가 압축되면 가비지 수집기는 포인터의 위치를 적절하게 수정합니다. 그러면 애플리케이션 루트는 개체의 새 위치를 가리킬 수 있습니다. 또한 가비지 수집기는 관리되는 힙의 포인터 위치를 연결할 수 있는 마지막 개체 다음에 지정합니다. 메모리는 컬렉션 과정에서 상당한 수의 도달할 수 없는 개체를 찾아내는 경우에만 압축됩니다. 관리되는 힙의 모든 개체가 컬렉션에서 유지되는 경우 메모리 압축이 필요하지 않습니다.

런타임에서는 성능 향상을 위해 큰 개체의 메모리를 별도의 힙에 할당합니다. 그러면 가비지 수집기는 큰 개체에 할당된 메모리를 자동으로 해제합니다. 그러나 메모리에서 큰 개체를 이동하지 않도록 하기 위해 이 메모리는 압축되지 않습니다.

세대 및 성능

가비지 수집기의 성능을 최적화하기 위해 관리되는 힙은 0, 1, 2의 3세대로 나뉩니다. 런타임의 가비지 수집 알고리즘은 컴퓨터 소프트웨어 업계에서 가비지 수집 체계를 실험하여 사실로 발견한 몇 가지 일반화를 기반으로 합니다. 먼저 관리되는 힙의 일부에 대한 메모리를 전체 관리되는 힙보다 압축하는 것이 더 빠릅니다. 둘째, 최신 개체의 수명은 짧아지고 이전 개체의 수명은 더 길어질 수 있습니다. 마지막으로, 최신 개체는 서로 관련되고 애플리케이션에서 동시에 액세스하는 경향이 있습니다.

런타임의 가비지 수집기는 0세대에 새 개체를 저장합니다. 애플리케이션 수명의 초기에 만들어져 수집 후에도 남아 있는 개체는 수준이 올라가 1세대와 2세대에 저장됩니다. 개체 승격 프로세스는 이 항목의 뒷부분에 설명되어 있습니다. 전체 힙보다 관리되는 힙의 일부를 압축하는 것이 빠르기 때문에 이 체계를 사용하면 가비지 수집기가 컬렉션을 수행할 때마다 관리되는 전체 힙에 대한 메모리를 해제하지 않고 특정 세대의 메모리를 해제할 수 있습니다.

실제로 가비지 수집기는 Generation 0(0세대)이 가득 차면 수집을 수행합니다. 0세대가 가득 찼을 때 애플리케이션이 새 개체를 만들려고 하면 가비지 수집기는 0세대에 개체에 할당할 주소 공간이 없다는 것을 발견합니다. 가비지 수집기는 개체에 대한 0세대의 주소 공간을 해제하기 위해 컬렉션을 수행합니다. 가비지 수집기는 관리되는 힙에서 모든 개체를 검사하는 대신 먼저 세대 0에서 개체를 검사합니다. 이는 새 개체의 수명이 짧은 경향이 있고 컬렉션이 수행될 때 0세대의 많은 개체가 더 이상 애플리케이션에서 사용되지 않을 것으로 예상되기 때문에 가장 효율적인 방법입니다. 또한 0세대 컬렉션만으로도 애플리케이션이 새 개체를 계속 만들 수 있도록 충분한 메모리를 회수하는 경우가 많습니다.

가비지 수집기는 세대 0의 수집 작업을 수행한 후, 이 주제의 앞에서 설명된 대로 도달 가능한 개체의 메모리를 압축합니다. 그런 다음 가비지 수집기는 이러한 개체를 승격하고 관리되는 힙에서 세대 1에 해당하는 이 부분을 고려합니다. 수집 후에도 존재하는 개체는 수명이 긴 성향이 있기 때문에, 이런 개체를 상위 세대로 승격시키는 것이 당연합니다. 따라서 가비지 수집기는 0세대 컬렉션을 수행할 때마다 1세대와 2세대의 개체를 다시 검사할 필요가 없습니다.

가비지 수집기가 0세대의 첫 번째 컬렉션을 수행하고 도달 가능한 개체를 1세대로 승격한 후, 관리되는 힙의 나머지 부분을 0세대로 간주합니다. 0세대가 가득 차서 다른 컬렉션을 수행해야 할 때까지 0세대의 새 개체에 대한 메모리를 계속 할당합니다. 이 시점에서 가비지 수집기의 최적화 엔진은 이전 세대의 개체를 검사해야 하는지 여부를 결정합니다. 예를 들어 0세대 컬렉션이 애플리케이션이 새 개체를 만들기 위한 시도를 성공적으로 완료하기에 충분한 메모리를 회수하지 못하는 경우 가비지 수집기는 1세대, 2세대의 컬렉션을 수행할 수 있습니다. 메모리 회수가 충분하지 않으면, 가비지 수집기는 2세대, 1세대 및 0세대의 컬렉션을 수행할 수 있습니다. 각 컬렉션 후에 가비지 수집기는 0세대에서 도달 가능한 개체를 압축하여 1세대로 승격합니다. 또한, 수집이 완료된 후에 세대 1에 존재하는 개체를 세대 2로 승격시킵니다. 가비지 수집기는 3세대만을 지원하기 때문에, 컬렉션을 통해 살아남은 2세대의 객체는 이후의 컬렉션에서 도달 불가능한 것으로 판단될 때까지 2세대에 머뭅니다.

관리되지 않는 리소스에 대한 메모리 해제

애플리케이션에서 만드는 대부분의 개체에 대해 가비지 수집기를 사용하여 필요한 메모리 관리 작업을 자동으로 수행할 수 있습니다. 하지만 관리되지 않는 리소스의 경우는 명시적으로 정리할 필요가 있습니다. 가장 일반적인 형태의 관리되지 않는 리소스로는 파일 핸들, 창 핸들 또는 네트워크 연결 등의 운영 체제 리소스를 래핑하는 개체를 들 수 있습니다. 가비지 수집기는 관리되지 않는 리소스를 캡슐화하는 관리되는 개체의 수명을 추적할 수 있지만 리소스를 정리하는 방법에 대한 구체적인 지식은 없습니다. 관리되지 않는 리소스를 캡슐화하는 개체를 만들 때 공용 Dispose 메서드에서 관리되지 않는 리소스를 정리하는 데 필요한 코드를 제공하는 것이 좋습니다. Dispose 메서드를 제공하면 개체를 완료할 때 개체의 사용자가 명시적으로 메모리를 해제할 수 있습니다. 관리되지 않는 리소스를 캡슐화하는 개체를 사용하는 경우 Dispose 를 인식하고 필요에 따라 호출해야 합니다. 관리되지 않는 리소스 정리 및 Dispose 구현을 위한 디자인 패턴의 예에 대한 자세한 내용은 가비지 수집을 참조하세요.

참고하십시오