LOH Fragmentation 관리와 최적화 방법: 24시간 돌아야 하는 시스템에서의 성능 최적화

이전에 작성한 글에서 C#Garbage CollectionLOH(Large Object Heap)SOH(Small Object Heap) 의 차이를 간단히 설명했었습니다. 아직 읽지 않으신 분들은 그 글을 먼저 확인해 보세요. 이번 글에서는 LOH Fragmentation 문제를 다루고, 이를 관리해야 하는 이유와 24시간 돌아야 하는 장비 같은 중요한 시스템 환경에서 LOH를 어떻게 최적화할 수 있는지, 구체적인 방법들을 설명할 것입니다.

LOH란 무엇인가?

Large Object Heap(LOH)85,000바이트 이상 크기의 큰 객체들을 저장하는 .NET의 메모리 영역입니다. SOH와는 달리 LOH는 큰 객체들을 처리하는데 특화되어 있으며, LOH의 객체들은 가비지 컬렉션 시 압축(compaction) 이 이루어지지 않습니다. 이로 인해 LOH에 할당된 객체들이 시간이 지나면서 fragmentation를 일으킬 수 있습니다.

LOH Fragmentation이란?

LOH Fragmentation 은 LOH에서 객체들이 할당되고 해제되면서 발생하는 메모리 단편화 현상을 말합니다. LOH에 할당된 객체들은 크기가 크기 때문에, 한 번 할당되면 그 크기만큼 연속된 메모리 공간을 차지하게 됩니다. 그러나 시간이 지나면서 여러 객체가 할당되고 해제되면서, 메모리 내에 빈 공간이 흩어지게 됩니다. 이로 인해 새로운 큰 객체를 할당하려고 할 때, 연속된 메모리 공간을 찾기 어려워지며 단편화가 발생하게 됩니다.

LOH Fragmentation 시나리오

LOH단편화 또한 일반적인 메모리 단편화와 발생하는 시나리오가 유사하지만, 아래의 이미지를 보면 LOH가 어떻게 단편화되는지 잘 설명되어 있습니다.


Source (Figure. 13)

  1. After Full GC:
    가비지 컬렉션이 실행된 후 LOH에서 메모리는 깔끔하게 정리되고 빈 공간이 발생합니다. 이 공간은 새로운 큰 객체를 할당할 수 있는 여유가 있지만, 할당된 객체들이 크기 때문에 그 자리가 다시 채워지지 않으면 빈 공간으로 남게 됩니다.
  2. After New Allocation:
    새로운 객체가 할당되면 LOH에 빈 공간이 재사용됩니다. 그러나 이 재사용된 공간은 작은 조각으로 남게 되며, 이 조각은 다시 사용되지 않을 가능성이 높습니다. 왜냐하면 그 공간은 85,000바이트 이하의 크기로, LOH의 크기 기준에 맞지 않기 때문입니다. 따라서 이 작은 조각은 다시 재사용되지 않으며, 단편화된 메모리로 남게 됩니다.
  3. After New Allocation (더 많은 할당):
    새로운 객체들이 계속 할당되면, 이전에 재사용된 작은 빈 공간들이 점차 재활용되지 않게 되고, LOH 내 단편화가 더욱 심해집니다. 이때 큰 객체를 할당하려고 시도하면 빈 공간을 찾는 데 어려움이 생깁니다. 결국 새로운 큰 객체는 연속된 공간을 찾지 못하고 새로운 메모리 공간에 할당될 수 있습니다.

LOH Fragmentation의 영향

위의 이미지는 LOH가 어떻게 빈 공간을 관리하는지, 그리고 새로운 객체 할당이 어떻게 단편화되는지 시각적으로 잘 보여줍니다. LOH에 단편화가 발생하면, 새로운 큰 객체를 할당할 때 필요한 연속된 메모리가 부족해지며, 이는 성능 저하나 OutOfMemoryException 과 같은 문제가 발생할 수 있습니다.

LOH Fragmentation 관리가 중요한 이유

LOH Fragmentation 을 관리하지 않으면 메모리 부족문제나 성능 저하를 초래할 수 있습니다. 특히 24시간 돌아야 하는 장비에서는 메모리 관리가 중요한 역할을 합니다. 예를 들어, 실시간 데이터 처리 시스템이나 금융 거래 시스템 등과 같은 중요한 시스템에서는 LOH 단편화가 발생하면 시스템 장애로 이어질 수 있습니다.

1. 성능 저하

LOH에서 발생한 단편화는 메모리 할당을 비효율적으로 만들고, 객체 배치가 어렵게 되어 성능 저하를 일으킬 수 있습니다.

2. 메모리 부족

LOH에서 메모리를 할당할 때 연속된 공간을 찾는 데 어려움이 생기면, 결국 OutOfMemoryException 을 발생시킬 수 있습니다. 이는 서비스 중단으로 이어질 수 있어 24시간 가동이 중요한 시스템에서는 치명적일 수 있습니다.

LOH Fragmentation을 방지하는 방법

그렇다면 LOH Fragmentation 을 어떻게 방지할 수 있을까요? 몇 가지 방법을 제시해 보겠습니다.

1. 큰 객체의 크기 분할

가능한 한 큰 객체를 여러 개의 작은 객체로 나누어 SOH에 할당되도록 유도하는 방법입니다. SOH는 LOH보다 메모리 관리가 효율적이고 단편화가 덜 발생하기 때문에, 큰 객체를 분할하면 LOH의 단편화를 줄일 수 있습니다.

2. 배열 관리 최적화

배열을 사용할 때, 배열의 크기를 예측하여 배열을 너무 크게할당하지 않는 것이 중요합니다. 배열의 총 크기가 LOH의 임계값을 넘지 않도록 신경 써야 하며, 배열이 여러 조각으로 나누어지지 않도록 합니다.

3. MemoryPool 사용

ArrayPool<T>MemoryPool<T>와 같은 객체 풀을 사용하면 LOH에서 발생할 수 있는 단편화를 방지할 수 있습니다. 객체 풀을 사용하면 반복적인 메모리 할당과 해제가 줄어들고, 미리 할당된 메모리 공간을 재사용할 수 있기 때문에 단편화 문제를 최소화할 수 있습니다.

4. Full GC 호출 최소화

GC.Collect() 메서드를 사용하여 Full GC 를 강제로 호출할 수 있지만, LOH는 Full GC가 필요하지 않으면 호출하지 않는 것이 좋습니다. Full GC는 성능에 큰 영향을 미치기 때문에, 불필요한 호출을 피해야 합니다.

5. 큰 객체 할당 최소화

LOH에 큰 객체를 자주 할당하지 않도록 하며, 큰 객체가 반드시 필요할 때만 할당하도록 합니다. LOH에 큰 객체가 많이 할당되면 Gen2 가비지 컬렉션이 자주 일어나고, 그로 인해 성능 저하를 겪을 수 있습니다.

LOH 단편화 방지의 중요성: 24시간 돌아야 하는 장비 예시

실제로 금융 거래 시스템 같은 24시간 돌아야 하는 시스템에서는 메모리 부족으로 인한 다운타임을 절대 허용할 수 없습니다. 이런 시스템에서는 LOH 단편화가 발생하면, 실시간 데이터 처리에 큰 영향을 미치고, 이는 결국 사용자 경험에 심각한 영향을 미칠 수 있습니다.

예시: 실시간 데이터 처리 시스템

예를 들어, 실시간으로 트랜잭션을 처리하는 시스템에서 LOH 단편화가 발생하면, 새로운 큰 객체를 할당하는데 필요한 메모리 공간을 확보할 수 없어 트랜잭션 처리 지연이 발생할 수 있습니다. 이로 인해 성능 저하나 서비스 중단이 발생하면, 큰 손실을 초래할 수 있습니다.

LOH 압축: GCSettings를 통한 LOH 압축

LOH Fragmentation 문제를 해결하기 위한 방법 중 하나는 LOH 압축을 수동으로 수행하는 것입니다. .NET Framework 4.5.1부터는 LOH를 수동으로 compact할 수 있는 기능이 제공됩니다. 기본적으로 LOH는 가비지 컬렉션 동안 자동으로 압축되지 않지만, GCSettings.LargeObjectHeapCompactionMode 속성을 사용하면 LOH를 명시적으로 압축할 수 있습니다.

LOH 압축 사용 예시

1
2
3
4
// LOH 압축 모드를 '한 번만 압축'으로 설정
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
// 가비지 컬렉션을 강제로 실행하여 LOH를 압축
GC.Collect();

LOH 압축의 장점과 고려 사항

LOH 압축을 명시적으로 호출하는 방법은 단편화를 줄이고 메모리 사용을 최적화하는 데 도움이 될 수 있습니다. 하지만 이 방법에는 몇 가지 성능 비용이 따르므로, 신중하게 사용해야 할 필요가 있습니다

  • 장점
  1. 메모리 압축을 통해 LOH 단편화를 줄일 수 있으며, 연속된 메모리 공간을 확보하게 되어 새로운 큰 객체의 할당이 수월해집니다.
  2. 특히 24시간 돌아야 하는 장비에서 메모리 부족 문제나 성능 저하를 예방하는 데 유용할 수 있습니다.
  • 고려 사항:
  1. LOH 압축은 가비지 컬렉션을 수행한 후에 발생하므로, 성능 비용이 들 수 있습니다. LOH가 크고 복잡한 경우, 압축을 자주 호출하면 시스템 성능에 부정적인 영향을 미칠 수 있습니다.
  2. 압축을 자주 수행하는 것보다, LOH의 크기나 객체 할당 방식을 최적화하여 단편화가 발생하지 않도록 하는 것이 보다 효율적일 수 있습니다.

결론

LOH 단편화 는 메모리 효율성에 큰 영향을 미칠 수 있는 문제입니다. 특히 24시간 돌아야 하는 장비와 같이 높은 가용성과 성능이 중요한 시스템에서는 LOH의 단편화 문제를 반드시 해결해야 합니다.

큰 객체 분할, 배열 최적화, MemoryPool 사용 등을 통해 LOH 단편화를 예방하고, 성능 저하 없이 안정적인 시스템을 운영할 수 있도록 관리해야 합니다. LOH 관리에 신경 써서 효율적인 메모리 사용을 통해 높은 성능을 유지할 수 있도록 노력해야 합니다.