[Rust] 강제로 누수시키기 (Leaking)

Rust는 기본적으로 안전한 메모리 관리/해제를 지향하는 언어이긴 하지만, 사용하기에 따라서 이러한 제한을 파괴하며 사용하는 것도 항상 가능하다.
일단 이런 사용사례 자체가 절대 일반적인 것이 아님에 유의한다. 일반적인 서버 등 상용 프로그램에는 이런 로직을 사용할 경우가 별로 없을 것이다.

내가 생각하기에 의도적인 누수 동작을 사용할만한 부분은 보통 극한의 성능 최적화일 것이다.
힙의 할당/해제는 생각보다 무거운 연산이다. OS 수준에서 핸들링해주니 가벼워보이지만, 실제로 힙에 메모리를 할당하는 부분은 어셈블리로 수백-수천줄은 된다.

메모리 할당/해제로 인한 비용을 줄이는 방법에는 여러가지가 있을 것이다. 그러나 모두 성능을 올릴 수는 있지만 메모리 사용량을 희생해야한다는 trade off가 있다.

  1. 그냥 임의의 거대한 arena 객체를 상위에 할당해놓고 파라미터를 통해 상속받아서 쓰게 하면 그것만으로도 성능 최적화가 될 것이다. 가장 건전하고 side effect가 적은 방법이다.
  2. allocator를 직접 제어하는 것도 하나의 방법이다. 이건 현재로서 코드 전체에 영향을 준다.
    https://blog.naver.com/sssang97/223142654009
  3. 이 중에서 가장 음습한 방법이 mem::forget 같은 함수를 통해 누수를 강제로 발생시키는 것이다. 뒤를 보지 않아도 되는 상황에서만 사용할 수 있다.



mem::forget 함수

이건 말 그대로 메모리에 대한 것을 잊는(forget) 동작을 한다.

이러면 루프 내 변수 text는 루프 범위를 지난다고 해서 바로 정리가 되지 않는다. 누수가 발생하는 것이다.

사실 그리 거창한건 아니고, 내부적으로 코드를 보면 이렇다.

그냥 ManuallyDrop으로 한번 감싸는 것이다.
ManuallyDrop라는 이 특수한 객체는 컴파일러에게 drop 소멸자를 호출하지 않도록 명령한다. 소멸자를 호출하지 않으니 힙 할당이나 파일 정리 등 cleanup 작업을 일괄 하지 않는 것이다.

정리가 거의 되지는 않을테지만, 영원히 존재한다는 보장까지는 없다.

그리고 특이하게도 음침한 동작인데도 unsafe가 아니다. 이런 식으로 누수를 발생시키는건 Rc에서 상호참조로 누수가 발생하는 것과 같은 결의 의도된 동작이라고 보기 때문이다.

아무튼 일상적으로 사용할만한 기능은 아니다. FFI를 하면 drop 방지가 필요할 수도 있긴 한데, 그렇더라도 ManuallyDrop 객체를 직접 생성해서 직접 drop을 제어하는 것이 바람직한 사용 형태다.
이건 진짜 잊어버리는 용도라서 drop을 제어할 수도 없다.




ManuallyDrop

이건 drop을 스코프 단위에서 제한하지 않고 직접 제어하고 싶을때 사용하는 기능이다.
특정 값을 이걸로 감싸서 생성하면, 컴파일러는 drop 호출 대상에서 제외한다.

그 이후에는 drop 메서드 등을 호출해서 임의로 drop을 시킬 수 있다.
이건 unsafe다. 두번 이상 drop을 시도하면 문제가 발생할 수 있다.

좀더 권장하는 방법은 into_inner로 값을 꺼내는 것이다.

그럼 내부 값을 꺼내옴과 동시에 ManuallyDrop 타입이 사라져서 다시 자동 drop 대상이 된다.



참조
https://doc.rust-lang.org/nomicon/leaking.html
https://doc.rust-lang.org/std/mem/fn.forget.html