본문 바로가기
자료구조

메모리에 관해 생각해볼만한 거리

by 진득한진드기 2023. 10. 11.

가상 메모리는 어떻게 돌아갈까?

위에 질문은 정말 흥미롭지만 깊이 들어가기엔 꺼려지는 질문이기도 하다.

 

전에는 이론으로서만 공부했던 주제이지만, 정글 과정에서 동적메모리 할당을 직접 구현하면서 느끼는 건 가상 메모리는 정말 혁신적이라는 거다.

 

막연하게 그렇게 쓰는 게 편해서 라고만 생각했었지만, swap-in, swap-out을 구현하면서 메모리의 물리적 한계를 극복하는 것 또한 매우 놀라웠던 경험이었다.

 

전에도 올린 글에 묵시적 가용 리스트를 구현하여 메모리 배치알고리즘인 first-fit과 next-fit을 구현하여 메모리의 효율성을 테스트했었다.

 

묵시적 가용 리스트는 블록 경계를 구분하고 할당된 블록과 가용 블록을 구분하는 데이터 구조를 필요하여 한 블록에 크기를 #define으로 크기를 설정하고 경계 태크로 이용해 4가지 케이스를 이용해 표현했었다.

 

명시적 가용 리스트를 구현했을 때는 가용블록 내에 이전 블록을 가리키는 포인터와 조정 포인터를 포함하여 구현했다.

묵시적 할당기 보다는 더 실제로 사용하기 편하지만 블록 크기가 더 커져 내부 단편화 가능성이 더 올라가는 단점이 있었다.

 

마지막으로 분리 가용리스트를 구현함으로써 동적 메모리 구현 과제를 마쳤었는데, 크기 클래스별로 분리하여 저장하여 메모리 관리의 효율성을 높였었다. 

 

전에 보이저엑스 면접에서 내가 구현했던 동적 메모리 과제를 보며 다음과 같은 질문을 했다.

 

"만약 가비지 컬렉터를 구현하려고 하면 어떻게 구현할 건가요?"

 

나는 처음에 메모리를 할당할 때 이후에 마크가 되지 않은 가용 가능한 메모리들을 집약하면 된다고 생각했다.

 

그런데 이후에 들어온 질문은 나를 당황하게 했다.

 

"가비지 컬렉터가 공백을 통합하는 과정을 구현할 때 통합하는 기준을 어떤 거로 잡으실 건가요?"

 

처음에는 시간을 기준으로 잡고 일정 시간이 되면 가용하지 못하는 메모리를 그냥 집약하면 되지 않나 생각했지만,

시간이 오래 지난 후 참조해야 하는 메모리가 있다면 사용 할 때 생기는 자원 소모가 더 커서 비효율적으로 느껴질 것 같았다.

 

그러면 그냥 참조된 횟수를 메모리에 새긴 다음 0이 되면 제거하면 되겠다는 해결 방안이 떠올랐지만, 또 막상 참조 횟수를 추적하는 과정에서 드는 비용이 너무 클 것 같아 고민하다가 대답을 하지 못했었다.

 

면접은 이후에 결과에서 탈락했지만, 당시 면접은 나에게 생각할 거리를 주었다.

 

메모리 해제를 어떻게 효율적으로 할까?

 

검색을 해본 결과 Unmanaged Language인 C++에서는 내가 전에 생각했었던 식으로 Reference counting이라는 방식으로 

메모리 참조 횟수를 활용하여 메모리 누수를 방지한다고 한다.(그때 그냥 말할걸.......)

 

물론 단순하게 저렇게만 돌아가는 것이 아니라 굉장히 복잡하다.

 

Reference Counting은 강한 참조와 약한 참조를 구분하여 처리하는 것도 생각해야 하고, 침습성(침입성) 참조 카운팅 방식도 있다고 한다.

 

침습성 참조 카운팅 방식은 객체 자체가 참조 카운트를 관리하고, 객체의 클래스에 직접 카운트 관리 코드를 포함한다.

그리고 객체가 메모리에서 해제될 때 객체 자체가 처리하는 방식이다. 메모리 사용은 비침습성보다 낮지만 객체 해제 시 참조 횟수도 날아가기 때문에 약한 참조를 구현하지 못한다. (순환 참조문제)

 

비침습성 참조 카운팅은 카운트를 따로 관리하는 방법인데 참조 카운트 변수가 추가 되어 침습성이 더 크게 작용하지만, count 정보가 있기 때문에 약한 참조를 구현 할 수 있다.

 

면접 결과와는 다르게 오히려 나에게는 좋은 지식을 알려주었고, 다시 한번 선배들이 메모리를 어떻게든 효율적으로 사용하려고 했구나를 느낄 수 있게 되었다.