1

글타래: OS X의 메모리 사용 방식에 관하여

OS X의 메모리 사용 방식에 관하여

다음은 Apple의 Discussion Board에 올려진 OS X의 메모리 관리에 대해서 자세하게 설명해 놓은 글을 간추린 것입니다. 원래는 Barry Sharp씨가 MacFixit에 있는 Ted Landau씨에게 보낸 전자우편들의 내용 중에서 가져왔으며, 시스템의 메모리 사용 원리를 이해하는 데 큰 도움이 되리라 생각됩니다.

전자우편 하나

Ted씨에게:

가상 메모리(VM)는 말 그대로 "가상"의 것이라서 실제로는 존재하지 않는답니다. 이 말은 가상 메모리가 디스크 공간을 조금이라도 차지하고 있지는 않다는 뜻입니다.

그래서, 시스템이 바꿔치기(swapping) 작업을 행하지 않았다면, 사용자로서는 스왑(swap) 파일의 크기나 그 위치에 대해서는 걱정할 필요가 전혀 없습니다. 바꿔치기 작업은 터미널의 top 명령으로 보이는 결과 중, 윗부분 마지막 줄에 있는 "0(0) pageouts" 값으로 확인할 수 있습니다. 또 다른 쓸만한 터미널 명령으로 vm_stat이 있습니다. 이 명령은 Pageout(실제 메모리에서 디스크로 떨어져 나간) 횟수도 보여주는데, 이 pageout 값은 실제로 메모리가 스왑 파일로 대치된 것을 나타냅니다. 바꿔치기 작업으로 들어오고 나가는 것은 page 덩어리(chunk)로 계산되는데, page 한 덩어리는 크기가 4096바이트입니다.

실제 메모리가 스왑 파일로 대치되었다면 이것은 실제 메모리가 용량을 초과해서 점유되었다는 뜻입니다. 빈번한 실제 메모리의 초과 점유를 방지하기 위한 가장 좋은 해법은 동시에 실행되는 애플리케이션들의 수를 줄이거나 더 많은 메모리를 설치하는 것일 겁니다. 실제 메모리가 용량을 초과해서 점유될 때, OS는 사용할 수 있는 메모리 공간을 마련하기 위해 현재 사용되는 않는(inactive) 메모리 공간을 찾아서 스왑 파일로 복사해놓게 되며, 이는 나중에 다시 스왑 파일에서 실제 메모리로 복사가 이루어지게 됩니다.

이러한 Unix 시스템 가상 메모리의 지속적이고 과도한 바꿔치기 작업은 결코 좋은 현상이 아니고 모든 수단을 마련해서 피해야 하겠지만, 여의치 않을 때에는 메모리 과 점유 현상에 의한 스왑 파일의 생성은 피할 수 없을 것입니다.

만약 pageout의 횟수가 0이 아니면서 또한 급격하게 늘어난다면, 메모리를 추가하거나 동시에 실행되는 애플리케이션들의 숫자를 줄여야 할 것입니다.

그리고 스왑 파일을 따로 위치시키고 설정하는데 드는 시간과 노력은 쓸데없는 짓이며, 이것은 과 점유로 말미암은 메모리의 근본 문제를 그냥 덮어두려는 것과도 같습니다.

또한, 다중 CPU 시스템에서는 바꿔치기 작업이 진행되는 동안 CPU가 다른 작업으로 바쁜 경우라도 바꿔치기 작업으로 인한 성능저하는 없답니다. 바꿔치기 작업과 CPU 작업은 동시에 진행될 수 있으며, 오로지 CPU가 바꿔치기 작업으로 디스크에서 읽고 쓰는 작업이 끝마치기를 기다리면서 대기하는 상황이 생길 때에만 바꿔치기 작업으로 말미암은 성능의 저하 문제를 보일 것입니다. 이 경우에는, 스왑 파일을 아주 빠른 장치에 위치시키는 것으로 득을 볼 수도 있을 것입니다.

대부분의 OS X 사용자들을 위해 제가 해 드릴 수 있는 충고는 스왑 파일의 위치와 설정은 그대로 내버려두고 컴퓨터에 항상 사용 가능한 충분한 실제 메모리가 설치되어 있도록 하시라는 겁니다.

전자우편 둘

Ted씨에게:

저의 "가상 메모리의 스왑파일과 그와 관련한 OS X 성능"에 관한 글을 읽은 후에 많은 분이 덧붙이신 글들에 대한 저의 생각들을 여기에 옮겨 봅니다.

의견 1: 얼마나 많은 RAM을 설치했든 간에 OS X는 모든 RAM을 소비해 버리는 것 같다.
의견 2: 사용 시간이 증가하면서 시스템의 성능도 함께 향상되는 것 같다.

비록 제가 X 커널 소스 코드를 살펴보지는 않았습니다만, 왜 이런 의견들이 돌고 있는지, 그리고 의견들이 어째서 옳은 얘기인지도 쉽게 짐작할 수 있을 것 같습니다.

먼저, 주제에서 약간 벗어난 얘기로 9.1 시절로 돌아가 봅시다. 9.1에서는 현재 OS X에서 이루어지고 있는 것과 관련해서 두 가지의 설정 선택사항들이 있었습니다.

이것은 바로 디스크 캐쉬와 RAM 디스크 기능이었습니다.

디스크 캐쉬는 기본값으로 설정되거나 변경될 수도 있었습니다. 이 캐쉬에는 자주 사용되는 디스크 데이터 혹은 그냥 디스크에 쓰여지는 데이터를 위해 사용되었습니다. 이것은 애플리케이션들이 필요하면 데이터를 바로 읽어들일 수 있도록 해서 디스크에서 읽어들이는 작업을 단축하고자 한 것입니다. 물론, 메모리 대 메모리 전송은 디스크 대 메모리의 속도 보다는 빠를 것입니다. 여기서 주지해 둘 것은, 바로 이 디스크 캐쉬는 고정된 것이라는 점입니다. 그 크기는 절대로 변하지 않습니다. 만약 이것을 더 크게 한다면, 자동 애플리케이션들을 위한 메모리의 공간은 줄어들게 됩니다. 반면, 너무나 작게 설정되어 있다면, 별로 큰 이득을 얻을 수 없을 겁니다. 그리고 데이터를 메모리에 담아두는 것은 약간의 문제를 안고 있어서, 만약 시스템이 오류로 멈춰버린다면 RAM에 담아두었던 데이터는 잃어버리게 되고, 재시동 후에도 복구할 수 없게 됩니다.

RAM 디스크 기능도 디스크 캐쉬와 그 성격이 비슷해서, 그 크기가 고정되어 있고, 또 애플리케이션들이 사용하게 될 소중한 메모리의 공간을 사용하게 됩니다. 이것의 용도는 몇몇 애플리케이션들이 데이터 파일들을 반복적으로 재사용 해야 할 때 유용하게 사용될 수 있습니다. 만약 이 데이터 파일들이 RAM 디스크에 모두 얹어서 사용할 수 있다면 느린 디스크의 데이터 전송 속도를 피할 수도 있어서 커다란 이점을 얻을 수 있었습니다.

OS X에서는 앞의 두 가지 기능들이 표면상으로는 존재하지 않습니다.

하지만, X의 기저(UNIX kernel)에서는 사용자에 의한 어떠한 간섭 없이도 두 가지 기능 모두를 제공하고 있습니다. 이것이 바로 파일 시스템 버퍼 캐쉬(file system buffer cache)입니다. 가장 큰 차이점은 이 버퍼 캐쉬의 크기는 유동적이라는 것입니다. 그래서, 처음에는 약간의 작은 크기로 시작해서 I/O 요구에 따라 증가하고 감소할 수 있으며 애플리케이션들의 메모리 요구량도 시간에 따라 변하게 됩니다.

이것은 I/O 데이터를 디스크로 저장하고 읽어들이면서 완충 기억해 놓는 연유로 해서 '버퍼 캐쉬(buffer cache)'라 불립니다. 애플리케이션이 데이터를 쓸 때, 처음에는 애플리케이션의 버퍼 메모리 구역에 저장되고 나중에는 라이브러리의 정해진 과정에 따라 OS 커널(kernel)에 의해 애플리케이션의 버퍼 내용을 디스크에 복사하도록 요청됩니다. 커널은 처음에는 무조건 파일 시스템 버퍼 캐쉬에 복사하게 되어 있답니다. 만약 버퍼 캐쉬의 공간이 더 필요하게 되면 사용되지 않는 빈(free) 메모리에서 모자란 부분을 채우게 됩니다. 이렇게 되면, 터미널의 top 명령에서 보이는 쓰이지 않는(free) 메모리의 값은 줄어들게 됩니다. 시간이 지나감에 따라 나중에 커널은 이 데이터를 - 더러운 버퍼(dirty buffer)라 말해지는 - 적당한 디스크 위치에 복사하게 됩니다. 제가 일기로는 이것의 주기는 대략 30초 간격을 두고 일어나며 이 작업은 디스크로 동기화(sync-ing) 한다고 불립니다.

한동안 재시동 없이 OS X를 계속 사용하게 되면 커널의 파일 시스템 버퍼 캐쉬에는 가장 많이 필요하거나 혹은 자주 사용되는 데이터들로 채워지게 됩니다. 이것은 왜 시스템을 오래 사용하면 할수록 더 향상된 성능을 갖는 것처럼 보이는지를 설명하는 데 도움이 될 것입니다. 이제는 작업에 필요한 대부분의 데이터들은 커널의 버퍼 캐쉬인 메모리에 자리하게 되고 이는 디스크에서 일일이 데이타를 읽어들일 필요가 없게 된다는 의미입니다. 물론 이것은 성능의 향상에 큰 도움이 됩니다.

앞에서도 말했듯이, 커널은 버퍼 캐쉬를 필요에 따라서 사용되지 않는 메모리에서 가져와서 사용하게 됩니다. 이것은 시스템의 작업량에 따라 단시간 혹은 긴 시간 동안에 터미널의 top 명령에서 보이는 사용 가능한 모든 RAM이 시스템에 의해 사용되고 있는 것처럼 보이게 되는 이유이기도 합니다.

또 한 가지 말해두고 싶은 것은, 커널 버퍼 캐쉬가 아주 많이 증가해서 설치된 RAM 대부분을 잠식했다고 해서 성능상의 큰 피해는 없다는 것입니다. 만약 새로운 애플리케이션이 실행되면 커널은 필요한 만큼의 버퍼 캐쉬를 내어주게 됩니다. 처음에는 새로운 애플리케이션의 메모리 요구량을 받아들일 수 있는지를 확인하기 전에 '더럽혀지지 않은' 버퍼 캐쉬 중 일부분을 내놓습니다. 모든 더럽혀지지 않은 버퍼 조각들을 다 내놓은 후에도 어플리케이션을 위한 더 많은 메모리가 필요하다면, 버퍼 캐쉬 중 더럽혀진 부분을 디스크에 복사해 놓으면서 얻어진 메모리 공간을 새로운 애플리케이션에 내어줍니다. 이 작업은 새로운 애플리케이션에 의해 요청된 모든 메모리가 충당되어서야 비로소 멈추게 됩니다. 이런 식으로 해서 커널 버퍼 캐쉬의 크기는 줄어들게 되며, 나중에는 어떤 정해진 최소의 크기까지 줄어들 겁니다. 이 경우, 커널은 다른 사용되지 않는(inactive) 메모리를 찿게 됩니다. 여기에는 놀고 있는 애플리케이션의 메모리도 포함되는데, 이 경우에는 커널은 새로운 애플리케이션의 메모리 요구량을 충족시키기 위해 놀고 있는 애플리케이션의 메모리를 디스크로 그대로 복사해 놓기(page out) 시작합니다. 이런 커널 활동을 페이징(paging) 혹은 바꿔치기(swapping)라고 부릅니다.

커널이 바꿔치기를 실행하면 이것은 컴퓨터에 설치된 실제 메모리가 과 점유된 상태라는 뜻입니다. 이러한 지속적인 바꿔치기는 시스템 전체의 성능에 부하를 주어서 작업 반응 속도가 더디게 만들고 과도한 디스크 I/O(읽고/쓰기) 탐색 작업들을 수행하게 됩니다. 이러한 형태의 작업은 터미널의 top 명령으로 볼 수 있는 "pageouts" 값에 바로 표시가 됩니다. 만약 이 값이 0이 아니고 짧은 시간 안에 급격하게 늘어난다면 과도한 바꿔치기가 일어나고 있다는 뜻이며, 결코 바람직한 현상은 아니랍니다.

만약 과도한 바꿔치기가 일어난다면 더 많은 RAM을 설치하거나 혹은 컴퓨터의 작업량을 줄여야 합니다. 물론 시스템은 계속 작업을 수행하겠지만, 최적의 성능을 보여주지는 않게 됩니다.

이것으로 왜 OS X 시스템이 재시동 없이 시간이 지남에 따라 더 좋은 성능을 보여주는 것처럼 보이는지 그리고 왜 충분할 것 같은 RAM을 설치하였어도 시스템이 모든 메모리를 소비하고 있는 것처럼 보이는지를 잘 설명해 주리라 생각됩니다.

좋은 한 예로 터미널 애플리케이션을 사용해서 커널 버퍼 캐쉬가 실제로 사용되는 것을 확인할 수도 있습니다. 방법은 터미널에서 커다란 크기의 파일 복사 작업을 수행하면 됩니다. 큰 파일로는 스왑파일이 적당할 것입니다. (이 작업을 수행하기 위해서는 root 권한이 필요합니다.) 또한 top 명령이 실행되는 동안 제2의 터미널 창을 열어 놓으면 편리합니다.

cp /var/vm/swapfile0 ./bigfile

만약 top 명령으로 이 복사 작업 전에 비어 있는(free) 메모리 용량이 100MB 정도로 표시되어 있었다면, 이 작업으로 빈 메모리 용량이 4~5MB 정도로 급격하게 줄어드는 것을 확인하실 수 있을 겁니다. 이것은 커널이 버퍼 캐쉬를 위해 거의 모든 사용 가능한 빈 메모리를 소비하였기 때문입니다.

이제 ./bigfile을 지웁니다.

rm ./bigfile

이렇게 하면 top 명령으로 보인 결과에는 어떤 일이 일어날까요? 여기서는 갑자기 사라져 버렸던 빈 메모리가 다시 채워진 것을 확인하실 수 있을 겁니다. 이것은 복사해 두었던 ./bigfile을 지웠기 때문에 커널이 더는 ./bigfile을 담아두었던 버퍼 캐쉬의 공간이 필요하지 않아서 디스크에 기록해 둘 필요가 없어졌기 때문입니다.

그러므로 저의 충고는

가) top 명령으로 보이는 빈(free) 메모리가 너무 작아서 걱정하실 필요는 없으며

나) pageouts 값에 주의를 기울이시고, 만약 이 값이 시간이 지나면서 급격하게 증가한다면 컴퓨터의 작업하중을 줄이거나 혹은 작업하중을 줄이는 것이 불가피하다면 추가 RAM을 설치해 주십시오.

다) 스왑파일의 크기를 줄이거나 다른 곳으로 옮겨두는 작업에 시간을 낭비하지 마십시오 -- 어쩌면 흥미로운 작업이 될 수도 있습니다만 일반 사용자에게는 어쩌면 쓸데없는 짓이랍니다. 그냥 간단하게 과도한 바꿔치기 작업만 피하시면 됩니다.

마지막으로 -- 제가 생각하기에는 OS X는 최소한도로 적은 양의 빈 메모리를 항상 유지하려 한다는 점입니다. 제 경우에는 3MB 이하로 내려간 것을 본 적은 없습니다. 제가 일기로, 이것은 메모리가 꼼짝달싹할 수도 없는 상황을 피하기 위한 것으로 아주 위급한 명령을 실행하는 데 필요한 메모리를 따로 저장해 놓은 것이라고 볼 수 있습니다.

아래는 제가 OS X 커널 소스 코드를 가지고 있지도 않고 직접 자세하게 실험해 본 적도 없지만 아는 한도 내에서 저의 의견들을 몇 가지 적어 놓은 것이랍니다.

1. (사실) 매번 OS X가 시동할 때마다 시스템은 스왑 파일의 조각들을 제거합니다 -- 예) swapfile0, swapfile1, swapfile2, ..., swapfileN

2. (사실) 매번 OS X가 시동할 때마다 시스템은 /var/vm/swapfile0 파일을 생성하며 제가 알기에는 그 크기는 80MB입니다.

3. (추측) OS X가 swapfile0 파일을 생성할 때 디스크에 연속적인 한 묶음으로 생성되도록 강제되지는 않을 것이며, 하드 디스크의 분절도에 따라 여기저기 흩어져서 기록될 것입니다.

4. (추측) OS X가 메모리를 바꿔치기(pages/swaps)할 때, 일정한 형태의 I/O를 사용해서 한 묶음(page라 불림)에 4096바이트 크기로 데이터가 전송됩니다. 이것은 아마도 HFS+ 형태의 최소 할당 크기가 4096바이트인 연유일 겁니다. 한 건의 I/O 요청으로 OS X가 다중의 4096 =바이트 묶음들로 바꿔치기 되는지는 불분명하지만, 만약 아니라면 바꿔치기는 하나의 4096바이트 묶음들로 전송될 겁니다.

5. (사실) 만약 커널 버퍼 캐쉬가 하나의 연속된 디스크 주소 영역에 있는 여러 4096바이트 묶음들로 구성되어 있으며, 이 여러 묶음들을 바꿔치기할 때 만약 커널이 여러 묶음들을 적당한 순서로 구성하고 단 하나의 I/O 요청을 실행하게 되면 훨씬 빠른 속도를 보여주게 될 것입니다.

6. (사실) 애플리케이션의 메모리는 RAM 전반에 흩어져서 있으며, 연속되어서 기록될 필요는 없습니다.

7. (사실) 대부분의 애플리케이션들은 메모리 영역 재진입 코드 부분들을 다른 애플리케이션들과 공유하고 있으며, 일반적으로 이런 코드 부분들은 자주 사용되기 때문에 절대로 바꿔치기 되어서 디스크에 기록되지는 않습니다.

8. (사실) 만약 OS X가 사용자의 메모리 요구에 부응하기 위해 계속하여 바꿔치기 작업을 하게 된다면 시스템의 반응 속도는 한때 밑바닥으로 처박힐 수도 있습니다.

이러한 현상은 top 화면에서 pageouts 값을 확인하면 알 수 있습니다.

계속적인 바꿔치기의 들락거림은 결코 좋은 현상이 아니며 메모리가 과 점유된 상태를 나타냅니다. 어떤 이유로 이것이 불가피해서, OS X가 각각의 4096바이트 묶음들이 아닌 연속된 4096바이트 묶음들로 바꿔치기해서 디스크에 기록한다는 가정하에 분절되지 않은 스왑파일은 어느 정도 이점이 있을 수도 있습니다만, 그렇지 않다면 분절되었든 아니든 간의 차이점은 전혀 없답니다. 어떤 경우이든, 이 상황은 스왑파일의 위치를 변경하는 것보다는 더 많은 RAM을 추가해 줌으로써 해결할 수 있습니다.

이것은 고속도로의 차량 정체 현상과 비슷해서, 만약 고속도로가 지속적으로 정체된다면 해결책은 고속도로로 진입하는 차들의 숫자를 제한하거나(작업량을 축소하거나) 혹은 길을 넓히거나 더 많은 고속도로를 건설하는 것(더 많은 RAM을 추가하는 것)뿐이라는 사실과 같겠지요.

제 추측에는 OS X가 스왑파일을 하드 디스크에 생성할 때 연속적으로 기록할 것으로 생각합니다만, 만약 이것이 맞는다면 스왑파일을 다른 곳에 있는 것은 아무런 이득도 없을 겁니다.

OS X 스왑파일들의 기본 배치와 관련해서 추가 정보를 찾게 되면 나중에 다시 연락드리겠습니다.

그럼, 어쩌면 장황할지도 모르는 저의 설명이 도움되셨으면 좋겠습니다.

이 글은 저의 UNIX OS 경험에 비추어 설명해 드렸음을 알려 드립니다.

Barry Sharp 씀.

다음은 터미널에서 top 명령을 실행하면 보이는 용어들에 대한 간단한 설명입니다.

PhysMem은 그냥 컴퓨터에 설치된 실제 메모리인 RAM을 뜻합니다.

1. Wired = 메모리를 점유하고 있으면서 절대로 바꿔치기 작업으로 디스크에 옮겨질 수 없는 영역 (메모리에 고정된 부분으로 예를 들어 OS 코드의 일부분이 될 수도 있습니다.)

2. Active = 최근 N 초 동안 읽힌 메모리 영역

3. Inactive = 최근 N 초 동안 읽힌 적이 없었던 메모리 공간으로 추가 메모리 요구 상황이 있을 시에는 가장 먼저 바꿔치기 될 수 있는 영역입니다.

4. Used = Wired + Active + Inactive

5. Free = 어떤 작업 혹은 커널에 의해 점유되지 않은 메모리 영역

6. VM = 가상 메모리 (실제 존재하지 않는 메모리 양으로 작업들이 잠재적으로 사용 점유할 수 있는 메모리 영역의 최대치로 실제로는 거의 요청되지 않습니다.)

top 명령으로 보이는 수치들은 실시간 결과과거 기록들을 같이 보여주게 됨을 기억해 두시기 바랍니다. PhysMem 항목에는 top 명령이 커널에게 요청했을 당시의 실제 값들을 보여주게 됩니다만 pageins과 pageouts 값은 컴퓨터가 시동했을 때부터 측정된 값들을 보여주게 됩니다.

따옴 - Mac OS X Hints

다른 글타래에도 올려두었던 메모리 관련 유용한 터미널 명령들을 덧붙입니다.

- 메모리 사용량 확인

sm=$(top -ocpu -Otime -R -l1 | grep 'PhysMem' | cut -c 11-75);
vm=$(top -ocpu -Otime -R -l1 | grep 'VM' | cut -c 5-9);
echo "SM Usage: $sm VM Size: $vm"

- 스왑 파일들(swap files)의 전체 크기 확인

size=$(/usr/bin/du -hc /var/vm/swap* | grep 'total' | awk {'print $1'});
echo "Total size of swap files: $size"

- Page out된 메모리 용량 확인

po=$(vm_stat | grep 'Pageouts' | awk '{print $2"*4096/1048576"}' | bc);
echo "Size of Page outs: $po MB"

위 명령들은 GeekTool에 등록해 놓고 주기적으로 확인하면 더 간편하답니다. wink

+ = ²