Memory Allocation

메모리가 부족한 것을 어떻게 확인할 수 있는지, 그리고 부족한 경우에 커널은 어떻게 대처하는지 알아본다.

swap

swap 영역은 디스크의 일부분을 메모리처럼 사용하기 위해 만들어놓은 공간이다. 메모리가 부족할 경우에 프로세스는 swap 영역을 할당받아 사용하게 된다. 물리 메모리가 아니라 디스크를 사용하기 때문에 접근과 처리 속도가 떨어져서 성능 저하가 일어날 수 있다.

swap 영역은 다음과 같이 만들 수 있다.

$ swapon -s // swap 용량 확인
$ fallocate -l 64M /swapfile // swap 파일 생성
$ chmod 600 /swapfile // 권한 조정
$ mkswap /swapfile // swap 형식 적용
$ swapon /swapfile // 임시 적용

free 명령어로 swap 영역이 잘 만들어졌나 확인해본다

$ free -k
              total        used        free      shared  buff/cache   available
Mem:         498372       76440      329404         504       92528      379880
Swap:         65532       52144       13388

swap 메모리를 사용한다는 것은 시스템의 메모리가 부족하다는 뜻이기 때문에 어떤 프로세스가 swap 메모리를 사용하는지 알아내는 것은 시스템의 상황을 개선시키기 위해서 중요하다. 이걸 아는 방법 중 하나로 proc/<pid>/smaps 파일을 확인하는 방법이 있다.

7f7e187ae000-7f7e187af000 rw-p 00000000 00:00 0
Size:                  4 kB
Rss:                   0 kB
Pss:                   0 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:            0 kB
Anonymous:             0 kB
AnonHugePages:         0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  4 kB
SwapPss:               0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
VmFlags: rd wr mr mw me ac sd

위에서 보는 프로세스는 논리적 메모리 7f7e187ae000-7f7e187af000 사이에 4KB의 크기중에서 4KB가 swap 영역에 속해있는 것을 볼 수 있다. 이런 방법으로는 프로세스의 각 논리적 메모리를 전부 살펴봐야하기 때문에 불편하다. 하나의 프로세스가 사용하는 총 swap 영역을 한눈에 보려면 /proc/<pid>/status 파일을 보는 것이 더 편하다.

Name:	stress
State:	R (running)
Tgid:	1886
Ngid:	0
Pid:	1886
PPid:	1884
TracerPid:	0
Uid:	1000	1000	1000	1000
Gid:	1000	1000	1000	1000
FDSize:	64
Groups:	4 20 24 25 27 29 30 44 46 109 110 1000
NStgid:	1886
NSpid:	1886
NSpgid:	1884
NSsid:	1664
VmPeak:	  138556 kB
VmSize:	  138556 kB
VmLck:	       0 kB
VmPin:	       0 kB
VmHWM:	  131212 kB
VmRSS:	    1832 kB
VmData:	  131264 kB
VmStk:	     132 kB
VmExe:	      20 kB
VmLib:	    3000 kB
VmPTE:	      40 kB
VmPMD:	      12 kB
VmSwap:	      56 kB
HugetlbPages:	       0 kB
Threads:	1
SigQ:	0/1884
SigPnd:	0000000000000000
ShdPnd:	0000000000000000
SigBlk:	0000000000000000
SigIgn:	0000000000000000
SigCgt:	0000000000000000
CapInh:	0000000000000000
CapPrm:	0000000000000000
CapEff:	0000000000000000
CapBnd:	0000003fffffffff
CapAmb:	0000000000000000
Seccomp:	0
Speculation_Store_Bypass:	vulnerable
Cpus_allowed:	7fff
Cpus_allowed_list:	0-14
Mems_allowed:	00000000,00000001
Mems_allowed_list:	0
voluntary_ctxt_switches:	22
nonvoluntary_ctxt_switches:	383355

이 외에 전체 프로세스별로 사용 중인 swap 영역의 크기를 확인하려면 smem이라는 유틸을 사용하면 된다.

$ smem -t
  PID User     Command                         Swap      USS      PSS      RSS
 1885 ubuntu   stress --io 4 --vm 2 --vm-b       84        4        8      148
 1890 ubuntu   stress --io 4 --vm 2 --vm-b       84        4        8      148
 1887 ubuntu   stress --io 4 --vm 2 --vm-b       84        4        9      152
 1889 ubuntu   stress --io 4 --vm 2 --vm-b       84        4        9      152
 1884 ubuntu   stress --io 4 --vm 2 --vm-b       88        0       43     1424
 1599 ubuntu   /lib/systemd/systemd --user      724        4       58     1616
 1664 ubuntu   -bash                           1412     1304     1379     3040
 3605 ubuntu   /usr/bin/python /usr/bin/sm        0     6440     6715     8560
 1886 ubuntu   stress --io 4 --vm 2 --vm-b       52    34220    34239    34456
 1888 ubuntu   stress --io 4 --vm 2 --vm-b       52    37260    37279    37496
-------------------------------------------------------------------------------
   10 1                                        2664    79244    79747    87192

Memory Allocation (Buddy System)

커널은 버디 시스템을 통해서 프로세스에 메모리를 할당한다. 버디 시스템은 물리 메모리를 연속된(각 4KB) 메모리 영역으로 관리한다.

$ cat /proc/buddyinfo
Node 0, zone      DMA      5    205    163     40     10      9      0      0      0      0      0
Node 0, zone    DMA32    260   1447   3794   2564   1124    301     20      0      0      0      0

/proc/buddyinfo로 확인한 현재 시스템의 현재 상황이다. 각 존의 행들은 2의 배수로 나열되어 있다. 즉 각각 연속 1개의 페이지 크기별 버디, 연속 2개의 페이지 크기별 버디, 연속 4개의 페이지 크기별 버디..이다. 이걸 통해서 DMA 존의 가용 영역을 계산해보면 (4KB x 5)+(8KB x 205)+(16KB x 163)+(32KB x 40)+(64KB x 10)+(128KB x 9) = 7340KB 가 되는 것을 볼 수 있다. DMA32 존도 같은 방식으로 계산을 할 수 있고, 모든 존의 가용 영역을 합산하면 free 명령어에서 나온 free 영역과 유사한 값이 나올 것이다.

커널은 메모리의 요청이 발생했을 때 버디 시스템에서 가장 적당한 버디 리스트를 찾아 프로세스에 넘겨준다. 커널은 기본적으로 유휴 메모리가 있을 경우 캐시로 활용하려 하고, 메모리 사용 요청이 증가하면 캐시로 활용하고 있는 메모리를 재할당해서 프로세스에 할당한다. vm.swappinessvm.vfs_cache_pressure 등의 몇 가지 커널 파라미터를 조정하면 이런 커널의 동작 과정을 좀 더 세부적으로 조절할 수 있다.

  • vm.swappiness: 커널이 얼마나 공격적으로 메모리 영역을 swap 영역으로 옮기느냐를 결정하는 파라미터이다. 기본값은 60이다.
  • mv.vfs_cache_pressure: 커널이 메모리를 재할당할 때 PageCache를 더 많이 재할당할지 아니면 디렉터리나 inode 캐시를 더 많이 재할당할지 결정하는 파라미터이다. 기본값은 100이다.

메모리 누수 해결/증설

시스템이 swap 영역을 사용하고 있다는 것은 두가지로 해석할 수 있다. 첫번째로 시스템에 성능 저하가 일어나고 있다는 것, 두번째로 메모리가 현재의 워크로드를 수용하는데에 부족하다는 사실이다. 즉 시스템이 더 많은 메모리를 필요로 한다고 이해할 수 있는데, 이 때 취할 수 있는 액션은 또한 두가지다. 메모리를 증설하거나, 메모리 누수를 발견해서 해결하는 일이다. 여기서 메모리 누수를 확인하는 것이 더 먼저 행해져야 되는 이유는 메모리 누수가 일어나는 상황에서 메모리 양을 물리적으로 늘려봤자 시간을 조금 더 버는 것일 뿐 다시 메모리 부족 현상이 일어날 수 있기 때문이다.

일반적으로 메모리 누수를 의심할 수 있는 상황이 몇가지 있다. 메모리 사용량을 그래프로 그려봤을 때 시간이 지남에 따라 메모리 사용량이 계속적으로 증가하고 있거나, 평상시와는 다르게 순간적으로 메모리의 사용량이 폭증하는 경우에는 추적이 필요하다. 메모리 누수 디버깅에는 pmap이나 gdb와 같은 도구가 도움이 된다.

comments powered by Disqus