MessagePipe 의 구독 진단은 숫자도 위치도 틀릴 수 있다
Esc

    MessagePipe 의 구독 진단은 숫자도 위치도 틀릴 수 있다

    2026년 9월 25일 수정–Unity라이브러리메시징프로파일링

    씬을 넘겼는데 이전 씬 오브젝트의 핸들러가 계속 불립니다. 어딘가에서 Subscribe 가 돌려준 IDisposable 을 풀지 않은 것입니다. MessagePipe 는 이것을 찾으라고 진단 창(Window > MessagePipe Diagnostics)을 넣어 두었습니다.

    진단은 구독 수와 구독한 곳을 기록하지만, 둘 다 그대로 믿으면 안 됩니다. 비동기 브로커에서는 구독 수가 실제보다 작아지고, 구독한 곳은 네임스페이스 이름과 IL2CPP 설정에 따라 엉뚱하게 찍히거나 파일과 줄이 빠집니다.

    구독 수는 늘 세고, 구독한 곳은 켜야 기록한다

    구독 수(SubscribeCount)는 Interlocked.Increment 한 번으로 늘 셉니다. 구독한 곳은 옵션 EnableCaptureStackTrace 를 켜야 new StackTrace(true) 로 뽑아 전역 락 안에서 딕셔너리에 넣습니다. 창이 컨테이너를 찾을 수 있게 루트 스코프에서 GlobalMessagePipe.SetProvider 를 한 번 부릅니다.

    var options = builder.RegisterMessagePipe(o => o.EnableCaptureStackTrace = true);
    builder.RegisterBuildCallback(c => GlobalMessagePipe.SetProvider(c.AsServiceProvider()));
    구독 + 해제 1회 .NET 10 Mono IL2CPP
    false (기본) 152 ns / 56 B 270 ns / 56 B 348 ns / 64 B
    true 21,803 ns / 14,807 B 23,207 ns / 1,614 B 17,779 ns / 1,400 B

    구독 한 번이 17~23 µs 가 됩니다. 발행 경로에는 진단 코드가 없어서 발행은 옵션과 상관없이 0 B 입니다(람다 구독, 유니티는 플레이어 빌드).

    비동기 브로커는 정상 순서로 해제해도 음수가 된다

    씬 스코프가 닫히면 컨테이너가 브로커의 Core 를 Dispose 하고, 진단은 그 Core 의 구독 수만큼 한 번에 뺍니다. 그다음 오브젝트들이 OnDestroy 에서 구독을 풉니다. 흔한 순서입니다.

    브로커 구독 2개 Core.Dispose() 뒤 그 뒤 구독을 하나씩 Dispose
    동기 2 0 0
    비동기 2 0 −2

    구독 목록은 제대로 비었고 틀리는 것은 진단 숫자뿐입니다. 하지만 씬을 몇 번 오가면 음수로 내려가 있고, 그 뒤 새로 새는 구독이 생겨도 0 근처로 보입니다.

    구독한 곳은 MessagePipe 밖 첫 프레임이다

    구독한 곳을 고르는 코드는 스택을 위에서부터 훑다가, 네임스페이스가 MessagePipe 로 시작하지 않는 첫 프레임에서 멈춥니다. 라이브러리 안쪽을 건너뛰려는 것인데, StartsWith 라서 그 글자로 시작하기만 하면 라이브러리로 봅니다.

    IL2CPP 기본 설정은 파일과 줄을 남기지 않는다

    같은 코드를 IL2CPP 플레이어로 돌리면 구독한 곳에 메서드 이름만 찍힙니다. Player Settings 의 IL2CPP Stacktrace Information 이 기본값이면 스택 프레임에 파일과 줄이 없기 때문입니다.

    IL2CPP Stacktrace Information 구독한 곳으로 찍힌 값
    Method Name (기본) Probe.Run 처럼 메서드 이름만
    Method Name, File Name, and Line Number Probe.Run (at Probe.cs:줄 번호)

    설정을 바꿔도 구독을 감싼 작은 메서드가 인라인되어 빠지면, 그 한 칸 바깥 메서드가 구독한 곳으로 찍혔습니다. 에디터(Mono)에서는 그대로 찍혔습니다.

    주의할 점

    1. 스택 트레이스 옵션은 에디터에서 켜고 에디터에서 본다

    안 지키면 생기는 문제. 켠 채로 빌드하면 오브젝트를 꺼낼 때마다, 씬을 열 때마다 구독 한 번이 유니티에서 51~86배 느려집니다. 그렇게 켜도 IL2CPP 기본 설정에서는 파일과 줄이 빠져 쓸모가 적습니다.

    이렇게 쓴다. #if UNITY_EDITOR 로 감싸 에디터에서만 켜고, 누수가 의심되면 같은 흐름을 에디터에서 재현해 찾습니다. 옵션은 플레이를 시작하기 전에 정합니다.

    2. SubscribeCount 가 0 이라고 누수가 없다고 판정하지 않는다

    안 지키면 생기는 문제. 비동기 브로커는 스코프가 먼저 닫히고 구독이 나중에 풀리는 흔한 순서에서 구독 수를 한 번 더 뺍니다. 음수에 묻혀 새로 새는 구독이 0 근처로 보이고, “0 이니 괜찮다” 고 넘어가게 됩니다.

    이렇게 쓴다. 이 숫자는 씬 전환 전후의 변화 방향을 보는 참고값으로만 쓰고, 의심되면 구독한 곳 목록을 직접 봅니다.

    3. 프로젝트 네임스페이스를 MessagePipe 로 시작하지 않는다

    안 지키면 생기는 문제. MessagePipeGame 처럼 지은 프로젝트는 진단 창에 구독한 줄 대신 그 호출자가 찍혀, 누수를 찾으러 연 창이 엉뚱한 파일을 가리킵니다.

    이렇게 쓴다. 프로젝트 네임스페이스는 MessagePipe 라는 글자로 시작하지 않게 짓습니다. 이것 말고는 피할 방법이 없습니다.

    정리

    MessagePipe 의 구독 진단은 해제 누락을 찾게 해 주지만 숫자도 위치도 틀릴 수 있습니다.

    • 구독 수는 늘 공짜로 세고, 구독한 곳은 켜면 구독 한 번이 17~23 µs 가 됩니다.
    • 비동기 브로커는 해제에 닫힘 검사가 없어 구독 수가 음수로 내려갑니다.
    • 구독한 곳은 MessagePipe 로 시작하지 않는 첫 프레임이고, IL2CPP 기본 설정에서는 파일과 줄이 빠집니다.