Watch at Ease세상을 읽고, 일상의 답을 찾다.

기술 · 실용 가이드

mmap이 exported pointers 때문에 닫히지 않나요? 배열을 바꾸기 전에 모든 참조를 추적하세요

부모 뷰를 해제하거나 배열 이름을 지워도 슬라이스와 별칭이 남을 수 있습니다. 결과가 매핑을 빌리는지 독립 복사본을 소유하는지 입증하세요.

참조와 복사본 8개의 매핑 닫기 결과 일러스트
자체 실측 일러스트: 살아 있는 참조 6종은 닫기를 막았고 독립 복사본 2종은 종료 후 읽혔습니다. 이번 CPython3.12.14/NumPy2.3.5 샘플에 한정됩니다. Watch at Ease

mmap.close()의 BufferError는 빌린 쪽이 아직 메모리를 필요로 한다는 신호일 수 있습니다. 배열 생성 방법을 바꿔 예외만 없애는 것으로 안전한 수명을 입증할 수는 없습니다.

매핑, 직접 뷰, 파생 뷰와 유지되는 모든 배열 참조의 관계부터 그려 보세요. 로컬 실험 8개는 이름 삭제, 빌림 종료, 독립 데이터 생성을 구분합니다.

1. 소유자와 외부로 전달되는 참조를 파악하세요

Python과 NumPy 버전, 매핑 생성자, 배열 dtype과 결과가 외부로 나가는 지점을 기록하세요. np.frombuffer는 입력 객체의 뷰를 만듭니다. 기본 배열 슬라이스는 다른 뷰를 만들 수 있고 alias = arr는 같은 배열에 이름을 하나 더 붙입니다. 어느 쪽도 독립 데이터 버퍼를 보장하지 않습니다.

정리 전에 API 계약을 선택하세요. 빌린 결과라면 모든 사용자가 끝날 때까지 매핑 소유자가 살아 있어야 합니다. 소유자보다 오래 남을 결과에는 독립 저장소가 필요합니다. 읽기 전용은 해당 뷰를 통해 허용되는 쓰기를 뜻하며 빌린 메모리를 독립 복사본으로 바꾸지 않습니다.

2. 로컬 실험 8개에서 확인한 결과

Linux의 CPython 3.12.14와 NumPy 2.3.5로 각 실험에 0부터 7을 담은 새 8바이트 익명 매핑을 만들었습니다. 직접 memoryview는 release() 전까지 닫기를 막았습니다. 부모를 해제해도 슬라이스 뷰가 남으면 닫히지 않았고 슬라이스를 해제한 뒤 닫혔습니다. 파생 읽기 전용 memoryview도 같았습니다.

np.frombuffer(mm,dtype=np.uint8)의 배열이 살아 있는 동안 닫기가 막혔습니다. arr를 삭제해도 arr[:2]를 보관하면 닫히지 않았고 alias = arr를 남겨도 같았습니다. 이 단순한 CPython 샘플에서는 남은 NumPy 참조를 제거하면 닫혔습니다. 캐시, 컨테이너, 클로저나 순환 참조가 있는 코드에서도 del arr만으로 정리가 보장된다는 뜻은 아닙니다.

3. 복사본은 수명 계약을 바꿉니다

일곱 번째 실험은 owned = arr.copy()로 독립 uint8 배열을 만들었습니다. 빌린 배열을 제거하자 매핑이 닫혔고 이후에도 owned.tolist()는 0부터 7을 반환했습니다. 여덟 번째는 owned = mm[:2]로 독립 bytes를 만들었으며 종료 뒤에도 0과 1을 유지했습니다. 추가 할당이 필요한 복사 결과이며 더 이상 매핑과 계속 동기화되지 않습니다.

원본이 유효할 때 외부로 나가야 하는 영역만 복사하세요. 지역 이름 하나를 삭제해도 다른 호출자의 별칭이 무효화되지는 않습니다. NumPy 배열의 내부 base 참조 사슬을 수동 해제하거나 닫기 오류를 숨기거나 보호 검사를 피하려고 생성자만 바꾸지 마세요. 제로 카피가 필요하면 명시적인 애플리케이션 계약으로 소유자 수명을 연장하세요.

4. 종료와 남길 데이터를 따로 검증하세요

memoryview에서는 유지되는 각 자식 뷰와 부모를 정해진 범위에서 종료하세요. 컨텍스트 관리자는 실제 관리 대상으로 둔 뷰에만 적용됩니다. NumPy에서는 소유자를 닫기 전에 모든 슬라이스와 별칭 사용을 끝내야 합니다. 실패하면 객체 참조 관계를 조사하고, 닫기를 반복하거나 가비지 컬렉션만으로 참조가 없음을 증명했다고 가정하지 마세요.

실험 8개 모두 각자의 정확한 정리 뒤 닫기 성공을 확인했습니다. 종료 후에는 독립 복사본만 읽었고 매핑 해제 뒤 빌린 포인터를 읽지 않았습니다. 실제 파일, 공유 메모리 unlink, 동시 사용자나 다른 플랫폼은 검사하지 않았습니다. 실제 애플리케이션에서는 종료 경로와 복사 결과 값을 따로 확인하세요. 파일 플러시와 영속성은 별도 계약이며 남은 내보내기 뷰를 해결하지 않습니다.

매핑 소유자와 참조 및 독립 복사본의 수명 일러스트
자체 소유권 일러스트: 제로 카피 결과에는 유효한 소유자가 필요하며 독립 복사본은 종료 경계를 넘을 수 있습니다. 닫기 성공과 데이터 소유권을 따로 검증합니다. Watch at Ease