2년차 개발자의 메모리 누수 해결 고민 과정을 까먹지 않기 위해 기록한 글 입니다.
다소 미숙하고 허접한 과정일 수 있지만,
더 좋은 아이디어의 영감이 되고자 공유합니다.
댓글창은 언제나 열려있습니다. 감사합니다.
메모리 누수 탐지에 필요한 것들에 대해 얘기해보고자 한다.
디버깅 할 때 제일 중요한 것이다. 근본 원인 파악이 제일 우선이 되고,
현상 재현이 가능하면 반복적인 테스트와 함께 해결책을 찾을 수 있다.
개발자는 상상을 현실로 만들어내는 힘이 있다.
그러나 절대 추측하지말고 상상하지 말자, 데이터와 사실로 판단하자.
다음은 메모리 사용량 데이터와 사실로 판단하는 데에
도움이 되는 리눅스 명령어들이다.
free -m
total used free shared buff/cache available
Mem: 39700 11617 5147 371 22936 27274
Swap: 0 0 0
sar -r 1
free -m
total
: 설치된 총 메모리 크기.used
: 사용중인 스왑 크기 (total - free - buff/cache) (total - available)free
: 사용되지 않은 스왑 크기 (total - used - buff/cache)shared
: 여러 프로세스에서 사용할 수 있는 공유 메모리.buffers
: 커널 버퍼로 사용중인 메모리cache
: 페이지 캐시와 slab으로 사용중인 메모리buff/cache
: 버퍼와 캐시를 더한 사용중인 메모리.available
: swapping 없이 새로운 프로세스에서 할당 가능한 메모리의 예상 크기.Resident Set Size
: OS에서 확인되는 프로세스의 메모리 사용량이다.int getProcSelfStatusRSS()
{
FILE* file = fopen("/proc/self/status", "r"); //pid 값 대신 self를 넣어 코드를 실행하는 프로세스의 pid 입력
int result = -1;
char line[128];
while (fgets(line, 128, file) != NULL)
{
if (strncmp(line, "VmRSS:", 6) == 0) { //6글자 'VmRSS:' 가 있는 라인
result = parseLine(line);
break;
}
fclose(file);
return result;
}
리턴값을 로그에 출력하고 로그를 파싱하여
그래프를 그릴 수 있도록 python 코드도 짠다.
bash 스크립트로 짜면 멋있었겠지만 어렵다. 그러니까 비슷한 인터프리터 언어 파이썬 코드를 짠다.
import sys
import re
import subprocess
cnt = 0
max_value_coll = []
for longFilePath in sys.argv[1:]:
cnt += 1
with open(longFilePath, "r") as log_file:
log_data = log_file.read()
log_lines = log_data.strip().split('\n')
res_size_list = []
res_change_list = []
virt_size_list = []
virt_change_list = []
for log_line in log_lines:
res_match = re.search(r'RES (\d+) kB \((-?\d+) kB\)', log_line) //로그 패턴
virt_match = re.search(r'VIRT (\d+) kB \((-?\d+) kB\)', log_line)
res_size = 0
if res_match is not None and virt_match is not None:
res_size = int(res_match.group(1))
if (res_size > 0) :
res_size_list.append(res_size)
file_name = longFilePath + "." + "resSize"
with open(file_name, "w") as file:
for element in res_size_list:
file.write(str(element) + "\n")
avg_res_size = sum(res_size_list) / len(res_size_list)
max_value = max (res_size_list)
max_value_coll.append(max_value)
print ("RES max : ", max_value)
print ("number of sample :", len(res_size_list))
print ("==================================================")
gnuplot_script = f"""
set terminal png
set output '{longFilePath}.resSize.png'
set ylabel "RES (kB)"
set yrange[0:{max(max_value_coll) + sum (max_value_coll) / len(max_value_coll) * 0.1* len(sys.argv) }]
"""
plot_commands = []
for i in range(1, len(sys.argv)):
plot_commands.append (f"'{sys.argv[i]}.resSize' using 1 with line title '{sys.argv[i].split('.mem')[0] + '.mem'}'")
gnuplot_script += "plot " + ", ".join(plot_commands)
gnuplot_script_bytes = gnuplot_script.encode()
process = subprocess.Popen(['gnuplot', '-'], stdin=subprocess.PIPE)
process.communicate(gnuplot_script_bytes)
그래프는 CLI 기반의 gnuplot으로 그래프를 출력할 수 있고,
png 파일로도 만들 수 있다.
세부설정으로 스케일도 세팅할 수 있으니 살펴보시길 바란다.
그렇게 그래프를 그리면 아래와 같은 그래프가 출력된다.
다양한 도구로 간편하고 빠르게 메모리 누수 의심 부분 확인하여
실제 메모리 증가 여부를 알 수 있다.
의심 부분을 찾는 법은 다음 글에 이어하도록 하겠다.
https://m.blog.naver.com/kmk1030/221982895216
https://blog.helperchoi.com/36