18. GCHandle/fixed 핀닝과 GC 압축(compaction)의 상호작용
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// 주제: GC 와 포인터의 상호작용 — fixed 문과 GCHandle 핀닝(pinning), 그리고 힙 압축.
//
// .NET GC 는 수집 시 살아있는 객체를 이동(compaction)시켜 단편화를 줄인다. 관리 객체의
// 메모리를 네이티브 포인터로 다루려면(소켓 I/O 버퍼, P/Invoke) 그동안 GC 가 그 객체를
// "움직이지 못하게" 고정해야 한다. 짧게는 unsafe `fixed` 문이, 오래는 GCHandle(Pinned)이
// 그 역할을 한다. 핀 고정은 이동을 막아 포인터를 안정시키지만, 힙 압축을 방해해 단편화를
// 유발한다.
//
// 과제:
// 1) 이 프로그램의 정확한 표준출력을 예측하라(네 줄).
// 2) fixed 블록 안에서 얻은 포인터가 왜 그 블록 동안만 유효한지, 블록을 벗어나면
// 무엇이 달라지는지 설명하라.
// 3) GCHandle(Pinned) 이 GC.Collect() 뒤에도 AddrOfPinnedObject() 를 불변으로
// 유지하는 이유를 설명하라.
// 4) 포인터로 쓴 값이 왜 managed 배열 뷰(buf[..])에도 그대로 보이는지(같은 저장소),
// 그리고 오래 핀 고정하는 것이 왜 단편화를 유발하는지 설명하라.
using System;
using System.Runtime.InteropServices;
public static class Program
{
public static unsafe void Main()
{
byte[] buf = new byte[4] { 1, 2, 3, 4 };
// (2) fixed: 블록 동안 핀 고정, 포인터로 직접 쓰기
fixed (byte* p = buf)
{
p[0] = 10;
p[3] = 40;
byte* q = p;
Console.WriteLine(p == q); // 줄1
}
Console.WriteLine($"{buf[0]} {buf[3]}"); // 줄2
// (3) GCHandle.Pinned: 더 오래 고정(interop/비동기 I/O)
var h = GCHandle.Alloc(buf, GCHandleType.Pinned);
try
{
IntPtr addr1 = h.AddrOfPinnedObject();
GC.Collect();
GC.WaitForPendingFinalizers();
IntPtr addr2 = h.AddrOfPinnedObject();
Console.WriteLine(addr1 == addr2); // 줄3
Marshal.WriteByte(addr1, 1, 99); // buf[1] = 99
}
finally { h.Free(); }
Console.WriteLine(buf[1]); // 줄4
}
} 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.