in reply to Re: Avoiding Memory Leaks with Win32::OLE (what leaks)
in thread Avoiding Memory Leaks with Win32::OLE

Memory usage grows roughly rapidly and monotonically. Since the original post, I have modified the Win32::OLE module on my machine to also delete the SAFEARRAY associated with Variants that have the VT_ARRAY and VT_BYREF flags set. Of course this contravenes the appropriate usage of the VT_BYREF, but when I use that module with this code, the memory usage is stable.

So it seems to me that it is possible to create a variant with the VT_BYREF flag that is the actual owner of the data. This should not be the case, as the whole idea of the VT_BYREF is that some other data structure owns the data and will take care of its cleanup

  • Comment on Re^2: Avoiding Memory Leaks with Win32::OLE (what leaks)

Replies are listed 'Best First'.
Re^3: Avoiding Memory Leaks with Win32::OLE (what leaks)
by Anonymous Monk on Oct 18, 2015 at 22:41 UTC

    Memory usage grows roughly rapidly and monotonically.

    Can you put some numbers on this?

    Since the original post, I have modified the Win32::OLE module on my machine to also delete the SAFEARRAY associated with Variants that have the VT_ARRAY and VT_BYREF flags set.

    How, what did you do?

    So it seems to me that it is possible to create a variant with the VT_BYREF flag that is the actual owner of the data. This should not be the case, as the whole idea of the VT_BYREF is that some other data structure owns the data and will take care of its cleanup

    maybe all thats needed is to expose API to "cleanup" this data as well, for the people who manage to create one of these things?

      Yes, here are the numbers. When the test program posted in the original question is run with the outermost loop going from (0 .. 10000), the program gains between 4K and 10K/second in the Task manager.

      What I modified. I added the code in OLE.xs to do a SafeArrayDestroy() on the SAFEARRAY* even if the variant has the VT_BYREF flag set. More specifically, immediately after the switch statement, but still inside the if() (at line 2333--maybe), in void ClearVariantObject(WINOLEVARIANTOBJECT *pVarObj), I added the following code,

      /* 14-Oct-2015 WDJ Added to avoid memory leak of byref arrays +*/ if ( vt & VT_ARRAY ) /* this is an array byref */ { /* printf("WDJ doing additional SafeArrayDestroy\n"); */ SAFEARRAY *psa = *V_ARRAYREF(pVariant); if ( psa ) SafeArrayDestroy(psa); } /* printf("WDJ Calling VariantClear in addition to VariantInit +\n"); */ /* VariantClear(pVariant); */

      The VariantClear(pVariant); that is commented out at the last line it likely not necessary, because the proper clean up was (hopefully) done manually above.

      But since I did the above change on 14 Oct, I am working on updating the code to follow the plan I outlined in the original post, which adds a ByRef() method that will create a VT_BYREF version of the Variant so it can be passed to subroutines, etc. Stay tuned.

      And yes, I e-mailed the original developer--no response yet. I'll ask around with my Python friends to see if they have done a similar thing.

      I have tested a new version of Win32::OLE that allows the following program to work ("work"=pass all module tests and not leak memory when running while monitored using the Task Mangler). Any thoughts about how to get this reviewed and maybe into the CPAN release?
      # OLE Variant memory test use strict; use Win32::OLE::Variant; for my $i ( 0 .. 10000 ) # Do many times so we have a lot of memory al +locations { my $v = Win32::OLE::Variant->new(VT_ARRAY|VT_BSTR, [1,1000]); $v->Put(1, "foo"); # Do something here to make $vr a separate variant that points to th +e data in $v # my $vr = Win32::OLE::Variant->new(VT_ARRAY|VT_BSTR|VT_BYREF, $v); +<-- LEAK in ver 0.1712 # With version 0.1714, we never make a Variant with VT_BYREF explici +tly. # Instead we make a variant without the VT_BYREF flag, then call B +yRef() # on it to return a reference. my $vr = $v->ByRef(); DoStuff( $vr ); # modify and see if $v has changed if ( $i == 0 ) { # next line should print: 0x2008, bar 1; 0x6008, bar 1 printf "0x%04x, %s; 0x%04x, %s\n", $v->_RefType(), $v->Get(1), $ +vr->_RefType(), $vr->Get(1); printf "0x%04x, %s; 0x%04x, %s\n", $v->_RefType(), $v->Get(2), $ +vr->_RefType(), $vr->Get(2); } undef $v; } # while this loop is running, check the Task Mangler for memory usa +ge print STDERR "Done.\n"; sub DoStuff { my $refVar = shift; for my $i ( 1 .. 1000 ) { $refVar->Put($i, "bar $i"); } }