메모리 관리

메모리 관리(memory management), 동적 메모리 관리, 동적 저장소 할당 또는 동적 메모리 할당은 컴퓨터 메모리에 적용되는 자원 관리의 한 형태이다. 메모리 관리의 필수 요구 사항은 프로그램이 요청할 때 동적으로 메모리 일부를 할당하고 더 이상 필요 없을 때 재사용을 위해 해제하는 방법을 제공하는 것이다. 이는 언제든지 하나 이상의 프로세스가 진행될 수 있는 (멀티태스킹) 모든 고급 컴퓨터 시스템에 중요하다.

메모리 관리의 효율성을 높이는 여러 방법이 고안되었다. 가상 메모리 시스템은 프로세스에서 사용하는 메모리 주소를 실제 물리적 주소와 분리하여 프로세스 분리를 허용하고 페이징 또는 보조 저장 장치로의 스와핑을 사용하여 사용 가능한 RAM 양보다 가상 주소 공간의 크기를 늘린다. 가상 메모리 관리자의 품질은 전체 시스템 성능에 광범위한 영향을 미칠 수 있다. 이 시스템을 통해 컴퓨터는 물리적으로 존재하는 것보다 더 많은 메모리를 사용할 수 있는 것처럼 보이게 하여 여러 프로세스가 이를 공유할 수 있도록 한다.

일부 운영체제, 예를 들어 버로스/유니시스 MCP 및 OS/360에서는 메모리가 운영 체제에 의해 관리된다. 다른 운영 체제, 예를 들어 유닉스 계열 운영 체제에서는 메모리가 응용 프로그램 수준에서 관리된다.

주소 공간 내의 메모리 관리는 일반적으로 수동 메모리 관리 또는 자동 메모리 관리로 분류된다.

  수동 메모리 관리 

섬네일|450px|외부 단편화의 예
할당 요청을 이행하는 작업은 충분한 크기의 사용되지 않는 메모리 블록을 찾는 것으로 구성된다. 메모리 요청은 힙 또는 프리 스토어라고 하는 큰 메모리 풀에서 일부를 할당하여 충족된다. 어느 시점에서든 힙의 일부는 사용 중이고 일부는 "자유"(사용되지 않음) 상태이므로 향후 할당에 사용할 수 있다.
C 언어에서 힙에서 메모리를 할당하는 함수는 이라고 하며 이전에 할당된 메모리를 가져와서 "자유"(향후 할당에 사용될)로 표시하는 함수는 라고 한다.

여러 가지 문제가 구현을 복잡하게 만드는데, 예를 들어 할당된 메모리 블록 사이에 많은 작은 간격이 있어 할당 요청에 사용할 수 없게 되는 외부 단편화가 있다. 할당자의 메타데이터 또한 (개별적으로) 작은 할당의 크기를 부풀릴 수 있다. 이는 종종 청킹으로 관리된다. 메모리 관리 시스템은 미결 할당을 추적하여 서로 겹치지 않고 메모리가 "손실"되지 않도록(즉, "메모리 누수"가 발생하지 않도록) 해야 한다.

 효율성 
구현된 특정 동적 메모리 할당 알고리즘은 성능에 상당한 영향을 미칠 수 있다. 디지털 이큅먼트 코퍼레이션이 1994년에 수행한 연구는 다양한 할당자에 관련된 오버헤드를 보여준다. 단일 메모리 슬롯을 할당하는 데 필요한 가장 낮은 평균 명령어 경로 길이는 52였다(다양한 소프트웨어에서 명령어 수준 프로파일러로 측정됨).

 구현 
할당의 정확한 위치를 미리 알 수 없으므로 메모리는 일반적으로 포인터 참조를 통해 간접적으로 액세스된다. 메모리 영역을 구성하고 청크를 할당 및 할당 해제하는 데 사용되는 특정 알고리즘은 커널과 연결되어 있으며 다음 방법 중 하나를 사용할 수 있다.

 고정 크기 블록 할당 

메모리 풀 할당이라고도 하는 고정 크기 블록 할당은 고정 크기 메모리 블록(종종 모두 동일한 크기)의 자유 목록을 사용한다. 이는 큰 개체를 할당할 필요가 없는 간단한 임베디드 시스템에 잘 작동하지만, 특히 긴 메모리 주소에서 단편화에 취약하다. 그러나 오버헤드가 크게 줄어들기 때문에 이 방법은 빈번한 할당 및 할당 해제가 필요한 개체의 성능을 크게 향상시킬 수 있으므로 비디오 게임에서 자주 사용된다.

 버디 블록 

이 시스템에서는 메모리가 단일 풀이 아닌 여러 메모리 풀에 할당되며, 각 풀은 특정 2의 거듭제곱 크기의 메모리 블록 또는 다른 편리한 크기 진행의 블록을 나타낸다. 특정 크기의 모든 블록은 정렬된 연결 리스트 또는 트리에 보관되며 할당 중에 생성되는 모든 새 블록은 나중에 사용하기 위해 해당 메모리 풀에 추가된다. 사용 가능한 크기보다 작은 크기가 요청되면 가장 작은 사용 가능한 크기가 선택되고 분할된다. 결과 부분 중 하나가 선택되고 요청이 완료될 때까지 프로세스가 반복된다. 블록이 할당되면 할당자는 블록을 불필요하게 분할하는 것을 피하기 위해 가장 작은 충분히 큰 블록으로 시작한다. 블록이 해제되면 해당 버디와 비교된다. 둘 다 자유 상태이면 결합되어 해당되는 더 큰 크기의 버디 블록 목록에 배치된다.

 슬랩 할당 

이 메모리 할당 메커니즘은 특정 유형 또는 크기의 객체를 수용하는 데 적합한 메모리 청크를 미리 할당한다. 이 청크는 캐시라고 불리며 할당자는 자유 캐시 슬롯 목록만 추적하면 된다. 객체를 생성하면 자유 캐시 슬롯 중 하나를 사용하고 객체를 소멸하면 슬롯을 자유 캐시 슬롯 목록에 다시 추가한다. 이 기술은 메모리 단편화를 완화하고, 적합한 메모리 부분을 찾을 필요가 없으므로 효율적이다. 왜냐하면 어떤 열린 슬롯이든 충분하기 때문이다.

 스택 할당 

많은 유닉스 계열 시스템과 마이크로소프트 윈도우는 힙 기반 과 유사한 방식으로 스택 메모리를 동적으로 할당하는 라는 함수를 구현한다. 컴파일러는 일반적으로 이를 스택 포인터를 조작하는 인라인 명령어로 번역한다. 이 방식으로 할당된 메모리는 를 호출한 함수가 반환될 때 자동으로 해제되므로 수동으로 메모리를 해제할 필요가 없지만, 오버플로의 위험이 존재한다. 그리고 alloca는 많은 시스템에서 보이지만 POSIX나 C 표준에는 없는 임시 확장 기능이므로 스택 오버플로 시의 동작은 정의되지 않는다.

오류를 보고하는 더 안전한 버전의 alloca인 는 마이크로소프트 윈도우에서 사용할 수 있다. 이는 의 사용을 요구한다. Gnulib은 동등한 인터페이스를 제공하지만, 오버플로 시 SEH 예외를 발생시키는 대신, 과도한 크기가 감지되면 malloc으로 위임한다. 유사한 기능은 glibc에서 의 사용과 같이 수동 계정 및 크기 검사를 사용하여 에뮬레이션할 수 있다.

 자동 메모리 관리 

애플리케이션에서 메모리를 적절하게 관리하는 것은 어려운 문제이며, 메모리 관리를 처리하기 위한 여러 가지 전략이 고안되었다.

 호출 스택 변수의 자동 관리 

많은 프로그래밍 언어 구현에서 프로그램의 런타임 환경은 서브루틴이 호출될 때 자동 변수라고 불리는 서브루틴의 비정적 지역 변수를 위해 콜 스택에 메모리를 자동으로 할당하고, 서브루틴이 종료될 때 해당 메모리를 자동으로 해제한다. 특별한 선언을 통해 지역 변수가 프로시저 호출 간에 값을 유지하거나 다른 서브루틴에서 지역 변수에 액세스할 수 있다. 지역 변수의 자동 할당은 사용 가능한 메모리에 의해 제한되는 깊이까지 재귀를 가능하게 한다.

 쓰레기 수집 

쓰레기 수집은 프로그램에서 더 이상 사용할 수 없는 객체에 할당된 메모리를 자동으로 감지하고, 해당 할당된 메모리를 자유 메모리 위치 풀로 반환하는 전략이다. 이 방법은 프로그래머가 프로그램에서 메모리 요청 및 메모리 해제를 명시적으로 코딩하는 "수동" 메모리 관리와 대조된다. 자동 쓰레기 수집은 프로그래머의 작업 부하를 줄이고 특정 종류의 메모리 할당 버그를 방지하는 이점이 있지만, 쓰레기 수집 자체는 메모리 자원을 필요로 하며, 프로세서 시간 측면에서 응용 프로그램과 경쟁할 수 있다.

 참조 횟수 계산 방식 

참조 횟수 계산 방식은 메모리를 가리키는 독립적인 포인터의 수를 세어 프로그램에서 더 이상 메모리를 사용할 수 없음을 감지하는 전략이다. 새 포인터가 메모리 조각을 가리킬 때마다 프로그래머는 카운터를 증가시켜야 한다. 포인터가 가리키는 위치를 변경하거나 포인터가 더 이상 어떤 영역도 가리키지 않거나 자체가 해제될 때 카운터는 감소해야 한다. 카운터가 0으로 떨어지면 메모리는 사용되지 않는 것으로 간주되고 해제되어야 한다. 일부 참조 횟수 계산 방식 시스템은 프로그래머의 개입이 필요하고 일부는 컴파일러에 의해 자동으로 구현된다. 참조 횟수 계산 방식의 단점은 순환 참조가 발생하여 메모리 누수를 유발할 수 있다는 것이다. 이는 "약한 참조"(참조 횟수 계산 방식에 참여하지 않지만 가리키는 영역이 더 이상 유효하지 않을 때 알림을 받는 참조) 개념을 추가하거나 참조 횟수 계산 방식과 쓰레기 수집을 함께 사용하여 완화할 수 있다.

 메모리 풀 

메모리 풀은 요청 또는 트랜잭션의 수명 주기와 같이 애플리케이션 상태를 기반으로 메모리를 자동으로 할당 해제하는 기술이다. 이 아이디어는 많은 애플리케이션이 메모리 할당을 생성할 수 있는 큰 코드 덩어리를 실행하지만, 해당 모든 덩어리가 더 이상 유효하지 않은 실행 지점이 있다는 것이다. 예를 들어 웹 서비스에서 각 요청 후에 웹 서비스는 요청 실행 중에 할당된 메모리가 더 이상 필요하지 않다. 따라서 메모리가 현재 참조되고 있는지 여부를 추적하는 대신, 메모리는 연결된 요청 또는 수명 주기 단계에 따라 할당된다. 해당 요청 또는 단계가 지나면 모든 관련 메모리가 동시에 할당 해제된다.

 가상 메모리가 있는 시스템 

가상 메모리는 메모리 구성을 물리적 하드웨어에서 분리하는 방법이다. 애플리케이션은 가상 주소를 통해 메모리에서 작동한다. 애플리케이션이 특정 가상 메모리 주소에 액세스하려고 시도할 때마다 가상 메모리 주소가 실제 물리적 주소로 변환된다. 이러한 방식으로 가상 메모리의 추가는 메모리 시스템 및 액세스 방법에 대한 세밀한 제어를 가능하게 한다.

가상 메모리 시스템에서 운영 체제는 프로세스가 메모리에 액세스하는 방법을 제한한다. 메모리 보호라고 불리는 이 기능은 프로세스가 자신에게 할당되지 않은 메모리를 읽거나 쓰는 것을 허용하지 않아 한 프로그램의 악성 또는 오작동 코드가 다른 프로그램의 작동을 방해하는 것을 방지하는 데 사용될 수 있다.

특정 프로세스에 할당된 메모리는 일반적으로 격리되어 있지만, 프로세스는 때때로 정보를 공유할 수 있어야 한다. 공유 메모리는 프로세스 간 통신을 위한 가장 빠른 기술 중 하나이다.

메모리는 일반적으로 액세스 속도에 따라 주 저장 장치와 보조 저장 장치로 분류된다. 메모리 관리 시스템은 다른 작업 외에도 이러한 두 메모리 수준 간에 정보를 이동하는 것을 처리한다.

 버로스/유니시스 MCP 시스템의 메모리 관리 

운영 체제는 컴퓨팅 시스템의 다양한 자원을 관리한다. 메모리 서브시스템은 메모리를 관리하기 위한 시스템 요소이다. 메모리 서브시스템은 하드웨어 메모리 자원과 자원을 관리하는 MCP OS 소프트웨어를 결합한다.

메모리 서브시스템은 시스템의 물리적 메모리와 가상 메모리(둘 다 하드웨어 자원의 일부)를 관리한다. 가상 메모리는 주변 장치, 일반적으로 디스크의 추가 공간을 사용하여 물리적 메모리를 확장한다. 메모리 서브시스템은 오버레이라고 알려진 프로세스에서 메인 메모리와 가상 메모리 간에 코드와 데이터를 이동하는 역할을 한다. 버로스는 가상 메모리의 최초 상업적 구현이었고(맨체스터 대학교에서 페란티 아틀라스 컴퓨터용으로 개발되었지만) B5000의 시스템 설계에 가상 메모리를 처음부터(1961년) 통합하여 외부 메모리 관리 장치 (MMU)가 필요 없었다.

메모리 서브시스템은 메모리 블록에 대한 논리적 요청을 사용 가능한 세그먼트 목록에서 찾을 수 있는 물리적 메모리 부분(세그먼트)에 매핑하는 역할을 한다. 각 할당된 블록은 세그먼트 기술자를 통해 관리된다. 세그먼트 기술자는 주소, 길이, 기계 유형, 그리고 블록이 주 메모리에 있는지 또는 기술자에 주어진 주소에서 로드되어야 하는지를 나타내는 p-비트 또는 '존재' 비트를 포함하는 관련 메타데이터를 포함하는 특수 제어 단어이다.

기술자는 작업이 참조된 블록을 오버플로하거나 언더플로하지 않도록 (일반적으로 버퍼 오버플로라고 알려진) 메모리 안전성과 보안을 제공하는 데 필수적이다. 기술자 자체는 특정 MCP OS 요소(NEWP의 UNSAFE 블록 지시어로 활성화됨)를 제외하고는 조작할 수 없는 보호된 제어 단어이다.

도널드 커누스는 ‘기본 알고리즘’의 2.5절 ‘동적 저장소 할당’에서 유사한 시스템을 설명한다.

 OS/360 및 후속 시스템의 메모리 관리 
IBM System/360은 가상 메모리를 지원하지 않는다. 작업의 메모리 격리는 선택적으로 보호 키를 사용하여 수행되며, 각 작업의 저장소에는 다른 키(감독자용은 0, 1–15는 다른 작업용)가 할당된다. OS/360의 메모리 관리는 감독자 기능이다. 저장소는 GETMAIN 매크로를 사용하여 요청하고 FREEMAIN 매크로를 사용하여 해제하며, 이는 작업을 수행하기 위해 감독자 (SVC)에 대한 호출을 발생시킨다.

OS/360에서 세부 사항은 시스템이 생성되는 방식에 따라 달라진다. 예를 들어 PCP, MFT, MVT에 따라 달라진다.

OS/360 MVT에서 작업의 영역 또는 공유 시스템 큐 영역(SQA) 내의 하위 할당은 하위 풀을 기반으로 한다. 하위 풀은 크기가 2KB의 배수이며, 보호 키로 보호되는 영역의 크기이다. 하위 풀은 0-255번까지 번호가 매겨진다. 영역 내의 하위 풀은 작업의 저장소 보호 또는 감독자의 키인 키 0을 할당받는다. 하위 풀 0-127은 작업의 키를 받는다. 초기에는 하위 풀 0만 생성되며, 다른 것이 메모리 요청에 지정되지 않는 한 모든 사용자 저장소 요청은 하위 풀 0에서 충족된다. 하위 풀 250-255는 작업을 대신하여 감독자의 메모리 요청에 의해 생성된다. 이들 대부분은 키 0을 할당받지만, 일부는 작업의 키를 받는다. 하위 풀 번호는 MFT에서도 관련이 있지만 세부 사항은 훨씬 간단하다.  MFT는 연산자가 재정의할 수 있는 고정 파티션을 사용하며 PCP는 단일 파티션만 있다.

각 서브풀은 서브풀 내에 할당되거나 사용되지 않는 메모리 블록을 식별하는 제어 블록 목록에 의해 매핑된다. 메모리는 충분한 크기의 사용 가능한 영역을 찾거나, 작업의 영역 크기까지 서브풀에 추가 블록을 할당하여 할당된다. 할당된 메모리 영역의 전부 또는 일부를 해제할 수 있다.

OS/VS1의 세부 사항은 MFT 및 MVT와 유사하고 OS/VS2의 세부 사항은 MVT와 유사하지만 페이지 크기가 4 KiB이다. OS/VS1과 OS/VS2 모두 공유 시스템 큐 영역(SQA)은 페이징할 수 없다.

MVS에서 주소 공간에는 추가로 페이지할 수 있는 공유 영역인 공통 저장 영역(CSA)과 두 개의 추가 개인 영역인 페이지할 수 없는 지역 시스템 큐 영역(LSQA) 및 페이지할 수 있는 시스템 작업 영역(SWA)이 포함된다. 또한 저장소 키 0-7은 모두 특권 코드에서 사용하도록 예약되어 있다.

 같이 보기 
 메모리 보호
 쓰레기 수집
 동적 메모리 할당
 페이징, 페이지 테이블
 포인터
 가상 메모리
 메모리 관리 장치

 각주 

 내용주

 외부 링크 

 메모리 관리 참조 
 리눅스 메모리 관리
 시스템 프로그래머를 위한 메모리 관리 


분류:컴퓨터 구조