C++ 메모리 단편화 - C++ memoli danpyeonhwa

공부용 이모저모

메모리 단편화 & 메모리 풀 본문

C++

메모리 단편화 & 메모리 풀

불타는버스 2021. 8. 6. 18:07

개요

기존 malloc이나 new연산자의 경우, 사이즈가 고정되지 않아

생성과 삭제를 반복할경우 메모리 단편화 현상이 생긴다. 대충 설명하자면 이런 형태이다.

C++ 메모리 단편화 - C++ memoli danpyeonhwa
컴퓨터의 힙공간이 이런 모양이라고 가정하자.
C++ 메모리 단편화 - C++ memoli danpyeonhwa
int -> short -> int 형으로 new 할당을 했을경우, 순서대로 4,2,4 순으로 힙에 쌓일 것이다.
C++ 메모리 단편화 - C++ memoli danpyeonhwa
중간에 short를 지웠으면 이런모양이 나오게 된다.

여기서 중요한점은, 시스템에서 딱히 저 빈공간을 당겨주거나 하지 않는다는 것이다.

C++ 메모리 단편화 - C++ memoli danpyeonhwa
그대로 int 형을 새로 할당할 경우, 2바이트 만큼의 구멍이 생긴채 메모리가 계속 쌓인다.

힙메모리 공간특성상 당연히 훨씬 큰 용량이 있겠지만, 사진처럼 용량이 12바이트라고 가정하자면,

남아있는 공간이 4바이트이기 때문에 int형을 한번더 만들 수 있다고 생각 할 수 있지만,

실제로는 떨어져있는 2바이트 공간 2개만이 남은것이라 생성이 불가능하다.

공간이 크다고 가정해도 저런식으로 크기가 다른 new와 delete를 반복한다고 하면 아래와 같은 모양이 나온다.

C++ 메모리 단편화 - C++ memoli danpyeonhwa
이런식으로 중간중간 구멍이 나는걸 메모리 단편화라고 한다.

PC에서도 이러한 것을 정리해서 공간을 넓히는 [디스크 조각모음]이라는 메뉴가 있다. 이런식으로 구멍이 난 데이터들을

하나로 모으는 작업이다.물론 실제로 당기고 늘리고 하는것은 아니고,메모리를 삭제 복사하는것이기 때문에, 오래걸린다.

이러한 현상을 해결하기 위해 존재하는 것이 메모리 풀이다.

하지만 큰 프로젝트에서 이 방법으로 완전히 막아내는건 불가능에 가깝고

어디까지나 최대한 메모리 단편화를 막아내기 위한 방법이다. 

메모리 풀

메모리 풀은 고정된 크기의 블록을 할당해 동적할당을 해준다.

이점은 new delete로 인한 단편화 현상으로부터 많이 안전해 질 수 있으며,

new delete를 사용하는 것보다 더 속도가 빠르다.

당연하지만 처음에는 똑같이 new를 하기 때문에 비슷하거나 조금 느리다.

메모리 풀의 종류는 고정 메모리풀, 가변 메모리 풀이 존재한다.

C++ 메모리 단편화 - C++ memoli danpyeonhwa

고정 메모리풀은 오브젝트(클래스) 단위로 관리한다.

가령 슈팅게임이라고 가정시,총알은 여러개가 존재 할 수 있는데,

게임에 나올 총알을 할당할 때마다 보관해 두었다가,

다시 on 시킮으로써 재활용 하는것이다.

그리고 일정 수치를 넘어가면, 더이상의 new를 하지 못하게 막아낸다.

새로운 것을 할당할때, 똑같은 크기를 지닌 false 시킨 객체 하나를

덮어씌우는 식으로 쓰기도 한다. 

정해진 만큼만 요청하기 때문에 단편화 현상이 발생할 일은 거의 없다.

또한 정해진 규율에 따르기 때문에 구현이 쉬운 축에 속한다.

DirectX에서는 이미지 크기가 다른 이미지들도 강제로 빈공간을 늘려 2의 배수 단위로 관리한다.

이 또한 고정 메모리풀 방식이라고 볼 수 있다. 대신 DX는 이런 구성의 단점으로 이미지크기가 커질수록

낭비하는 공간이 많아진다는 문제가 있기는 하다.(2048 x 2048을 1픽셀이라도 넘어가는순간 4096 x 4096이 된다)

C++ 메모리 단편화 - C++ memoli danpyeonhwa

가변 메모리 풀은 최초의 생성 당시에는 오브젝트의 베이스 규모로 생성하되,

이후에 추가되는 내용물에 따라 각 객체당의 할당크기가 늘어나게 한다.(사이즈의 제한은 있어야한다)

다형성을 이용해 관리하는것으로 생각하면 된다.

Free List라고도 불리며, 갯수 파악이 쉽다는 장점이 있다.

가변적으로 사용하는 것이기 때문에 on off 또한 없이 new delete로 사용한다.

사실상 new를 사용하는것과 차이가 없다시피한데, 이러한 방식은

오브젝트의 관리를 위해 사용하며, 보통 두가지를 같이 만들어 사용한다.

정리하면,

1.오브젝트의 사이즈가 확실한 객체의 경우 고정 메모리풀로 배치시킨다.

2.사이즈가 고정되지 않은 가변적인 메모리들을 가변 메모리 풀로 배치 시켜서 관리한다.

david's daily developer note

memory fragmentation

메모리 단편화와 관련한 이야기를 해볼까 한다.. 

우리는 우리의 프로그램을 사용하는 사람들의 PC가 슈퍼컴퓨터가 아닌 것을 알고 있다.

그렇지만, 우리는 메모리를 효율적으로 사용하기 위해서 얼마나 고민하고 있을까?

나는 될 수 있다면 new 키워드를 사용하지 않으려고 노력한다.

Native 개발자가 Low 레벨 메모리 접근을 꺼리는 이유는 여러 가지다.

나의 경우는 멀티스레드, 멀티코어 환경에서 안전성을 보장받지 못한다는 점과(그건 뭐.. 대부분 그렇긴 한데..)

경험상 메모리 할당의 남발이 메모리 단편화의 문제를 만들고,

결국 성능을 저하하는 원인이 될 수 있기 때문이다.

메모리 생명주기를 알 수 없는 런타임 메모리 할당이 스택 될 수 없고,

필연적으로 단편화가 발생할 것이라는 걱정이 있다.

"오래 쓰면 느려져요"라거나, "동시에 여러 개 켜면, 버벅거려요" 라는 것은

대부분 이런 문제가 아닐까 싶다.

(다른 원인으로, 코어를 한정해두면, 동일 제품이 여러 개 켜질 때, 병목이 발생할수도 있다.)

단순히 malloc과 free 메모리 탐색, 병합이 느리지는 않다.

malloc 과정에서 연속된 메모리 공간을 찾는 과정은 느리지 않다고 한다.

작은 블록들은 크기 별로 linked-list 형태로 관리되어 단순 상수 시간 검색으로 찾을 수 있다고 하고,

그 밖에 페이지 단위보다 작은 크기의 메모리 블록들은 O(m) 시간 탐색 가능한 trie 형태로 관리되며

페이지 단위보다 크면 운영체제 환경에 따라 빠른 접근 방법이 존재한다(virtualalloc).

또한, 메모리가 free 되면 연속될 수 있는 경우에는 병합되기 때문에 단편화의 문제가 줄어든다고 한다.

그러나 모든 free 메모리가 연속되어 병합될 수 없고

결국 사용할 수 없는 작은 블록들은 메모리 할당자에게 무시될 수 있다.

여기서 내부 단편화의 경우(주소 크기 단위로 할당하여 사용되지 않는 공간)는 메모리 할당에 관여하는 

개발자의 꼼꼼함이 필요하지만, 

외부 단편화의 경우(할당된 메모리 블록사이의 free블록)는 stress 테스트나 분석 도구를 활용하여

일정 기준 이상의 제품 상태를 만족시킬 때까지 테스트를 해야 한다.

프로그램이 런타임에 사용하는 In-Memory DB 크기를 알 수 있다면,

혹은 그려질 그래픽 메모리를 파악할 수 있다면, 메모리 풀을 만들어서 직접 관리하는 방법도

메모리 단편화를 줄일 방법이다. 이는 어차피 잡혀야 하는 메모리를 alloc, free를 

반복하여 관리하지 않고 큰 블록하나를 관리하고자 함인데, 프로그램이 실행하는 시점에,

시스템 상태에 따라서 동작하는 내부 옵션화로 접근할 수 있겠다.

추가로 힙 메모리 사용 시에 문제를 하나.. 언급하고자 한다.

바로, heap overflow vulnerability 문제이다.

주로 버퍼 오버플로우(buffer overflow)와 버퍼 오버런(buffer overrun)을 말한다.

heap 메모리 할당으로 기존 heap을 덮을 가능성이 있다는 말이다.

(heap은 높은 주소로 영역을 넓힌다. 주소 영역 상위에 주요 메모리가 있다면 덮어질 수 있다.)

제품 개발자가 이런 실수를 한다면, 프로그램은 단순히 죽을 것이고, 사용자에게 불편을 준다.

회사의 입장에서는 제품의 안정성으로 매출에 영향을 받을 수 있다는 점도 그렇지만,

Crack의 문제가 더욱 골치 아프다. 보통은 프로그램의 DB 등을 부숴버리기 위하여 취약점을 공격하지는 않는다.

프로그램이 주는 이익을 취하기 위해서 불필요한 결함을 만들 필요는 없기 때문이다.

보통은 사용자 권한이나, 제품 인증을 위한 메모리나, 스택을 지워버린다.

(실제로 dll.dll이라는 이상한 dll이 권한 인증 단계의 스택을 무시하도록 한 경우를 보았다...)

malloc performence

http://3dmpengines.tistory.com/1408

memory fragmentation

http://acezero79.blogspot.kr/2008/02/electronics-design-strategy-news.html

trie 

https://namu.wiki/w/%ED%8A%B8%EB%9D%BC%EC%9D%B4

heap overflow vulnerability

buffer overflow & buffer overrun

https://ko.wikipedia.org/wiki/%EB%B2%84%ED%8D%BC_%EC%98%A4%EB%B2%84%ED%94%8C%EB%A1%9C

http://janghw.tistory.com/entry/Heap-Buffer-Overflow

false sharing

http://www.smallake.kr/?p=2271