Perl에서는 변수와 데이터 구조에 대한 메모리를 참조 횟수 세기(reference counting) 메커니즘을 통해 관리합니다. 객체나 구조는 참조 횟수가 0이 될 때 해제됩니다.
그러나 이 메커니즘은 순환 참조를 조사하지 않습니다. 객체가 서로를 참조하면 참조 횟수가 0에 도달하지 않으며, 메모리가 해제되지 않습니다.
순환 참조를 정리하기 위해서는 다음과 같은 방법을 사용합니다:
Scalar::Util::weaken를 사용한 약한 참조(weak references). 약한 참조는 참조 횟수를 증가시키지 않아, 다른 강한 참조가 없으면 GC가 객체를 해제할 수 있습니다.약한 참조 생성 예시:
use Scalar::Util 'weaken'; my $parent = {}; my $child = { parent => $parent }; $parent->{child} = $child; weaken($child->{parent}); # 이제 parent->child->parent가 정리를 방해하지 않음
Perl은 Scalar::Util 모듈이나 수동 개입 없이 순환 참조를 "인식"하고 해제할 수 있나요?
아닙니다, Perl은 기본적으로 순환 구조를 자동으로 "수집"할 수 없으며, 이 메커니즘은 객체 그래프 구조 분석을 요구합니다(예: JVM 또는 Python의 GC와 같은). 따라서 항상 스스로 루프 정리에 신경 써야 합니다.
이야기
웹 애플리케이션의 세션 저장 서버에서 양방향 관계를 가진 User 객체들이 활발히 사용되었습니다. 수천 번 요청 후에 프로세스의 메모리가 증가하는 문제가 발생했습니다: $user->{session}->{user} = $user. 약한 참조 사용 후 역참조의 메모리 누수가 사라졌습니다.
이야기
LRU 알고리즘을 사용하는 캐시에서 객체들이 서로 링크된 체인을 가지고 있었습니다. 개발자들은 수동으로 링크를 초기화하지 않아, 서비스가 몇 일 동안 작동한 후 메모리가 급증하고 OOM으로 중단되었습니다.
이야기
복잡한 마이크로서비스에서 문서 저장 시 보고서를 생성할 때 eval 및 순환 참조를 가진 대규모 데이터 구조를 사용했습니다. 개발자들은 Perl의 자동 가비지 컬렉션을 기대했지만, 서버는 오래된 객체를 "기억"하여 일주일 후에 모든 사용 가능한 RAM을 소모했습니다. 진단 결과 순환 구조가 발견되었고, 각 보고서 후에 Scalar::Util을 사용했습니다.