2010-01-28 1 views
2

필자는 Apache에서 실행되는 기존 Perl CGI 페이지를 사용하여 대용량 Excel 스프레드 시트 상당의 데이터를 처리하고 필요에 따라 데이터베이스에 추가합니다. 데이터 그룹으로 처리되고 각 데이터 그룹이 데이터베이스로 전송됩니다.Perl CGI 스크립트에서 가능한 메모리 누수를 어떻게 디버깅 할 수 있습니까?

데이터베이스를 호출 할 때마다 시스템의 사용 가능한 메모리가 크게 줄어들어 메모리가 남아 있습니다. 마침내 '조기 종료 스크립트 헤더'오류가 발생하고 HTTP 코드 500이 클라이언트에 반환되면 메모리가 시스템으로 다시 해제됩니다.

(복잡한) 코드를 살펴보면 메모리 누수가 발생한 위치를 찾을 수 없습니다. 메모리가 어디로 갈지를 결정하는 데 사용할 수있는 트릭이나 도구가 있습니까?

+0

어떤 버전의 Perl 및 DBI입니까? 한 번에 새로운 준비된 성명서를 만들려고 할 때마다 기억 누출에 빠지기는했지만, 그렇게 나쁜 것은 아닙니다. – fennec

+0

CGI 스크립트를 한 번 호출 할 때 메모리가 폭발합니까? 아니면 결국 실패 할 때까지 반복되는 호출이 계속 증가합니까? –

답변

2

짧은 대답은 당신이 싫다는 것입니다. 답을 얻기 위해 실행할 수있는 사용하기 좋은 프로그램이 없습니다. 나는 더 많은 도움이 될 수 없다는 점에 유감 스럽지만 코드 등을 보지 않고서는 누군가가 줄 수있는 더 좋은 조언이 정말로 없다.

나는 당신의 특별한 상황에 대해 이야기 할 수는 없지만, 과거에 내가 한 일들은 여기에 있습니다. 문제의 원인이되는 일반적인 영역을 찾아내는 것이 중요합니다. 다른 디버깅 기법과 크게 다르지 않습니다. 보통 나는 이러한 것들에 대한 우아한 해결책이 없다는 것을 알게됩니다. 당신은 당신의 소매를 감아 서 팔꿈치가 얼마나 심할지라도 팔꿈치 깊숙한 곳에서 팔을 찌르십시오.

먼저 웹 서버 외부에서 프로그램을 실행하십시오. 커맨드 라인에서 여전히 문제가 발생한다면 행복 할 것입니다. 웹 서버에 문제가 없다는 것이 대부분입니다.웹 환경을 설정하는 래퍼 스크립트를 만드는 데는 약간의 작업이 필요할 수 있지만 환경을 재설정하기 위해 서버를 다시 시작하는 등의 작업을하지 않아도되기 때문에 훨씬 쉽습니다.

서버 외부에서 문제를 복제 할 수 없다면 다음 권장 사항을 계속 수행 할 수 있습니다. 더 성가신 일입니다. 그것이 웹 서버 문제이고 명령 줄에서 문제가 아니라면, 그 작업은 그 둘의 차이에 대한 발견이됩니다. 나는 그런 상황에 직면했다.

웹 서버에 문제가 없다면 디버깅 문제와 마찬가지로 스크립트를 양분하십시오. 로깅을 사용하도록 설정 한 경우이를 켜고 실제 메모리 사용을 기록하면서 프로그램이 실행되는 것을보십시오. 언제 폭발합니까? 일부 데이터베이스 호출을 좁히는 것 같습니다. 명령 줄이나 디버거에서이 코드를 실행할 수 있다면 메모리 증가 전후에 적절한 한 쌍의 중단 점을 찾아 점진적으로 서로 가깝게 만듭니다. 의심되는 데이터 구조의 메모리 크기를 확인하려면 Devel::Size과 같은 모듈을 사용할 수 있습니다.

거기에서 용의자를 좁히는 것입니다. 용의자를 찾으면 짧은 예제 스크립트로 복제 할 수 있는지 확인하십시오. 가능한 한 많은 공헌 요인을 제거하려고합니다.

불쾌감을주는 코드를 발견했다고 생각되면 문제가 아직 이해되지 않는 경우 코드를 표시하는 다른 질문을 할 수 있습니다.

정말 멋진가 싶으면 자신의 Perl 디버거를 작성할 수 있습니다. 그리 어렵지 않습니다. 명령문의 시작 또는 끝 부분에서 DB 네임 스페이스의 일부 서브 루틴을 실행할 수 있습니다. 당신은 당신이 의심스러워하는 것들에 대한 디버깅 코드 목록 메모리 프로파일을 가지고 있고 메모리 크기에서 점프를 찾는다. 다른 모든 것이 실패하지 않는 한 나는 이것을 시도하지 않을 것이다.

+0

이전에 Perl 디버깅을 모두 수행 한 방법은 그만합니다. 내가 들어 본 적이없는 정말 유용한 툴이 있었으면 좋겠다. 심층 전략에 감사드립니다. – Aaron

-1

문제가 Perl 코드에있는 경우 자체 또는 부모 노드를 가리키는 참조가있을 수 있습니다.

다음은이 동작을 보여주는 간단한 코드 샘플입니다.

{ 
    my @a; 
    @a = [\@a]; 
} 

일반적으로 부모 개체를 참조하는 개체 형태로 제공됩니다.

{ package parent; 
    sub new{ bless { 'name' => $_[1] }, $_[0] } 
    sub add_child{ 
    my($self,$child_name) = @_; 
    my $child = child->new($child_name,$self); 
    $self->{$child_name} = $child; # saves a reference to the child 
    return $child; 
    } 
} 
{ package child; 
    sub new{ 
    my($class,$name,$parent) = @_; 
    my $self = bless { 
     'name' => $name, 
     'parent' => $parent # saves a reference to the parent 
    }, $class; 
    return $self; 
    } 
} 
{ 
    my $parent = parent->new('Dad'); 
    my $child = parent->add_child('Son'); 

    # At this point both of these are true 
    # $parent->{Son}{parent} == $parent 
    # $child->{parent}{Son} == $child 

    # Both of the objects **would** be destroyed upon leaving 
    # the current scope, except that the object is self-referential 
} 

# Both objects still exist here, but there is no way to access either of them. 

가장 좋은 방법은 Scalar::Util::weaken입니다.

use Scalar::Util qw'weaken'; 
{ package child; 
    sub new{ 
    my($class,$name,$parent) = @_; 
    my $self = bless { 
     'name' => $name, 
     'parent' => $parent 
    }, $class; 

    weaken ${$self->{parent}}; 

    return $self; 
    } 
} 

모든 가능한 경우 하위 개체의 참조를 삭제하는 것이 좋습니다.

+0

이것은 메모리 누수를 확인하는 방법이 아닙니다. 이것은 op 코드가 메모리를 누출시킬 수있는 방법의 예입니다. Op는 플러그인이 C/C++로 작성되었다고 말하지 않았으므로이 경우 수동으로 리퍼런스 카운트를 증가시키고 감소시켜야합니다. – rook

-1

데이터베이스 작성 방법은 무엇입니까? DBI 패키지 또는 사용자 정의 래퍼 중 하나를 사용하는 경우 캐시 된 오브젝트 또는 캐시 된 변수를 플러시하는지 확인하십시오. 이러한 유형의 메모리 팽창 문제는 비교적 일반적이며 일반적으로 계속 유지되는 공유 객체 캐시를 대표합니다.

상황이 시도 :

  • 지우기 개체 변수를 그들과 함께 완료되면
  • 딥 사이클 데이터베이스 연결 (이 극단적이지만 연결하는 방법에 따라,이 문제가 해결 될 수 있습니다).
  • 한 번에 하나의 데이터 그룹 만로드하고 그룹간에로드하는 변수 또는 팩토리 객체를 플러시합니다.

일부 도움이 되길 바랍니다.

관련 문제