Perl은 자동 메모리 관리를 위해 참조 카운트 알고리즘을 사용합니다: 각 변수에는 참조 카운트가 있습니다. 참조 카운트가 0이 되면 메모리가 해제됩니다. 대부분의 경우 이는 투명하게 작동하며 — 사용하지 않는 변수는 범위를 벗어날 때 자동으로 삭제됩니다.
문제는 순환 참조가 발생할 때 발생합니다 (예: 객체가 자기 자신을 참조하거나 두 구조가 서로를 참조하는 경우). 이 경우 참조 카운트는 결코 0이 되지 않으며 메모리가 해제되지 않습니다.
누수를 방지하기 위해 Scalar::Util::weaken 모듈을 사용합니다 — 이는 참조 카운트를 증가시키지 않는 "약한" 참조를 생성할 수 있게 합니다.
예제:
use Scalar::Util qw(weaken); my $a = {}; my $b = { ref => $a }; $a->{ref} = $b; weaken($a->{ref}); # 이제 순환 강한 의존성이 없습니다.
Perl이 복잡한 상호 연관 구조가 있더라도 항상 자동으로 모든 사용하지 않는 메모리를 해제한다고 가정하는 것이 맞습니까?
답변 및 예제:
아니요! 순환 참조의 경우 Perl은 weaken을 사용하지 않으면 메모리를 자동으로 해제할 수 없습니다:
my $a = {}; $a->{self} = $a; # 순환 # $a는 자동으로 삭제되지 않으며 — 수동으로 끊거나 참조를 약화시켜야 합니다.
이야기 1: 대규모 Perl 웹 서비스에서 메모리 누수가 발생했습니다 — 사용자 세션이 해시에서 서로를 참조하고 있었으며 누군가가 약한 참조를 사용하지 않았습니다. 서비스는 하루가 지나면서 모든 자원을 소모하며 중단되고 재시작이 필요했습니다.
이야기 2: 사용자 및 그룹 객체 간에 순환을 생성하는 자체 작성 ORM이 있었으며, 각각 서로를 참조하고 있었습니다. 범위를 벗어난 후 객체는 메모리에 남아 있었고 — 서비스는 점차 수십 기가바이트까지 "부풀어 올랐습니다!"
이야기 3:
$self를 참조하는 클래스 메소드로서 익명 서브 프로그래밍(“클로저”)을 사용할 때 각 객체 생성 시 누수가 발생하며, 이후 분석기가 순환 참조를 발견하고weaken의 필요성을 지적할 때까지 문제였습니다.