Perl memory usage profiling and leak detection?

前端 未结 5 1916
名媛妹妹
名媛妹妹 2020-11-27 14:58

I wrote a persistent network service in Perl that runs on Linux.

Unfortunately, as it runs, its Resident Stack Size (RSS) just grows, and grows, and grows, slowly

相关标签:
5条回答
  • 2020-11-27 15:02

    I recently used NYTProf as a profiler for a large Perl application. It doesn't track memory usage, but it does trace all executed code paths which helps with finding out where leaks originate. If what you are leaking is scarce resources such as database connections, tracing where they are allocated and closed goes a long way towards finding leaks.

    0 讨论(0)
  • 2020-11-27 15:03

    A nice guide about this is included in the Perl manual : Debugging Perl memory usage

    0 讨论(0)
  • 2020-11-27 15:08

    What next to try (not sure if this would be best placed in a comment after Alex's question above though): What I'd try next (other than Devel::Leak):

    Try to eliminate "unnecessary" parts of your program, or segment it into separate executables (they could use signals to communicate, or call each other with command-line arguments perhaps) -- the goal is to boil down an executable into the smallest amount of code that still exhibits the bad behaviour. If you're sure it's not your code that's doing it, reduce the number of external modules you're using, particularly those that have an XS implementation. If perhaps it is your own code, look for anything potentially fishy:

    • definitely any use of Inline::C or XS code
    • direct use of references, e.g. \@list or \%hash, rather than preallocated references like [ qw(foo bar) ] (the former creates another reference which may get lost; in the latter, there is just one reference to worry about, which is usually stored in a local lexical scalar
    • manipulating variables indirectly, e.g. $$foo where $foo is modified, which can cause autovivication of variables (although you need to disable strict 'refs' checking)
    0 讨论(0)
  • 2020-11-27 15:19

    You could have a circular reference in one of your objects. When the garbage collector comes along to deallocate this object, the circular reference means that everything referred to by that reference will never get freed. You can check for circular references with Devel::Cycle and Test::Memory::Cycle. One thing to try (although it might get expensive in production code, so I'd disable it when a debug flag is not set) is checking for circular references inside the destructor for all your objects:

    # make this be the parent class for all objects you want to check;
    # or alternatively, stuff this into the UNIVERSAL class's destructor
    package My::Parent;
    use strict;
    use warnings;
    use Devel::Cycle;   # exports find_cycle() by default
    
    sub DESTROY
    {
        my $this = shift;
    
        # callback will be called for every cycle found
        find_cycle($this, sub {
                my $path = shift;
                foreach (@$path)
                {
                    my ($type,$index,$ref,$value) = @$_;
                    print STDERR "Circular reference found while destroying object of type " .
                        ref($this) . "! reftype: $type\n";
                    # print other diagnostics if needed; see docs for find_cycle()
                }
            });
    
        # perhaps add code to weaken any circular references found,
        # so that destructor can Do The Right Thing
    }
    
    0 讨论(0)
  • 2020-11-27 15:19

    You can use Devel::Leak to search for memory leaks. However, the documentation is pretty sparse... for example, just where does one get the $handle reference to pass to Devel::Leak::NoteSV()? f I find the answer, I will edit this response.

    Ok it turns out that using this module is pretty straightforward (code stolen shamelessly from Apache::Leak):

    use Devel::Leak;
    
    my $handle; # apparently this doesn't need to be anything at all
    my $leaveCount = 0;
    my $enterCount = Devel::Leak::NoteSV($handle);
    print STDERR "ENTER: $enterCount SVs\n";
    
    #  ... code that may leak
    
    $leaveCount = Devel::Leak::CheckSV($handle);
    print STDERR "\nLEAVE: $leaveCount SVs\n";
    

    I'd place as much code as possible in the middle section, with the leaveCount check as close to the end of execution (if you have one) as possible -- after most variables have been deallocated as possible (if you can't get a variable out of scope, you can assign undef to it to free whatever it was pointing to).

    0 讨论(0)
提交回复
热议问题