Author Topic: PJSR: memory leak?  (Read 7296 times)

Offline mschuster

  • PTeam Member
  • PixInsight Jedi
  • *****
  • Posts: 1087
PJSR: memory leak?
« on: 2015 January 10 20:27:21 »
When I repeatedly run a script from the Script Editor, I notice what appears to be a memory leak on Win 7 1123 on a small 4GB laptop. The Win 7 Resource Monitor displays a steadily decreasing amount of available memory with successive script executions. Eventually script execution fails due to a PI out of memory error, after many script executions. The Script Editor's Reset Javascript Runtime command does not free up memory. Closing all open image windows does not free up memory. The script does call Global.gc() periodically during its execution. Once the out of memory error occurs, I am forced to exit and relaunch PI. Are there any PJSR tools I can use to diagnose this problem? I have a gut feeling that the problem may be due to Matrix usage (something I have not used previously in scripts). This new script allocates many Matrix objects (1k+ on a typical execution).

Thanks,
Mike

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: PJSR: memory leak?
« Reply #1 on: 2015 January 11 05:10:30 »
We use the Matrix PCL class extensively in the PixInsight Core application and lots of tools. As far as I know, there is no memory leak in this class (it is a very simple and lightweight implementation of matrices; you can see the entire source code in the pcl/Matrix.h header).

This looks more like a JavaScript engine problem, probably something related to garbage collection. I'm going to investigate it thoroughly. Could you upload a script where I can reproduce this problem?
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline mschuster

  • PTeam Member
  • PixInsight Jedi
  • *****
  • Posts: 1087
Re: PJSR: memory leak?
« Reply #2 on: 2015 January 11 09:50:38 »
Hi Juan,

Here is the script and example (~300MB) data:

https://dl.dropboxusercontent.com/u/109232477/PixInsight/WavefrontEstimator.zip
https://dl.dropboxusercontent.com/u/109232477/PixInsight/Example.zip

Please follow these steps:

1. Open the icon WavefrontEstimator.xpsm
2. Double click the icon and change the script's file path to its local location. You may also clear the MD5 checksum.
3. Execute the icon in the global context. Verify the Parameters tab is set as shown below.
4. Select the Files tab and add the 16 intra files, the 16 extra files, and the bias master from their respective Intra, Extra, and Bias folders into their appropriate panes.
5. Select the Output folder as output directory. Verify the Files tab is now set as shown below.
6. Create a new instance to capture all of these settings.
7. Launch the instance in the global context and click on the Estimate button.
8. Once the estimation completes, click on the Dismiss button and close all open image windows.
9. Repeat steps 7. and 8. several times.

On Win 7 1123 on my 4GB laptop, Win 7 Resource Monitor reports a decrease in available memory of 140-180 megabytes on each script execution. I have about 2GB available for use on my laptop, after about 10 to 20 repetitions I see out of memory errors.

Also, you may compare console log output from script editor execution versus icon global execution to see differences.

Thanks,
Mike

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: PJSR: memory leak?
« Reply #3 on: 2015 January 12 12:35:23 »
Hi Mike,

Thank you for the script and test data. I have carried out several tests on Linux and Windows 8.1, with similar results. Memory consumption is somewhat worse on Windows but it tends to stabilize in my tests after several executions. Here is a sequence of free memory measurements (in MiB) after running your script on a Windows 8.1 Professional virtual machine (VMware WS 10.0.4) with 8 GiB of RAM:

7027.027
6558.781
6798.816
6250.934
6217.543
6282.281
6251.137
6306.367

Each value has been measured after closing all images and clearing the console. There are variations but the values are more or less consistent with what you see on your machine. However it seems to stabilize at about 6200-6300 MiB. On this machine, the amount of free memory can decrease by 400 MiB if I simply open Windows Explorer. After a while, the wasted memory is normally returned to the free amount.

I have traced deallocations of Matrix objects in the core application while your script is running, and it is quite curious how the JS engine performs garbage collections. The sequence of deallocations is always: 2048, 2560, 2048, 2560, ... For some reason the engine decides to call Matrix finalizing routines this way, but I have no idea why.

I don't think we have a memory leak here. It is just that your script allocates many large matrices and *lots* of variables. Here are several things that you can try to reduce memory consumption:


1. As a general rule: As soon as you are done with a large Matrix (or similar objects associated with large data blocks, such as Image), deallocate it. For example:

      function foo()
      {
         ...
         {
            ...
            let M = new Matrix( 1000, 1000 );
            ...
            // Force deallocation. Now we don't depend on the garbage collector.
            M.assign( 0, 0, 0 );
         }
         ...
      }


In the next version I'm going to add a new clear() method to force deallocation of Matrix and Vector objects. Image already has a free() method that you can use when necessary.


2. In the incoming version 6 of the ECMAScript specification (ES6), the let statement allows defining variables with block scope. Unlike var, which defines a function scope variable, objects declared with let get out of scope exactly as automatic variables do in C and C++. For example, instead of:

      for ( var i = 0; i < ... )

we can typically use:

      for ( let i = 0; i < ... )

The difference is that i is now local to the for loop body, and hence unknown outside the loop. Just the same as in C++:

      for ( int i = 0; i < ... )

For functions that use many variables and objects with associated large native data blocks, the let keyword may save memory because the garbage collector has more chances to release memory when variables get successively out of scope:

      for ( let i = 0; i < 100; ++i )
      {
         let M = new Matrix( ... );
         ...
      }


In this example, M will be unreferenced as soon as the current iteration of the for loop terminates. Declared with var, the M variable would be accessible within the whole function where it is declared.

Fortunately, we can use let in PJSR because SpiderMonkey 24 already implements this ES6 feature (actually, the Mozilla engine implements this keyword since version 1.7).

I know it can be a lot of work, but it may be interesting to use let instead of var in your script, at least for some critical routines, and see if this helps memory consumption.


Let me know if this helps somewhat. The JavaScript engine is a complex and difficult beast, especially regarding memory resources. I'm still concerned with this problem though, so I'll continue doing tests and trying to devise ways to improve it.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/

Offline mschuster

  • PTeam Member
  • PixInsight Jedi
  • *****
  • Posts: 1087
Re: PJSR: memory leak?
« Reply #4 on: 2015 January 12 13:31:00 »
Hi Juan,

Thank you so much for investigating. I will try your clear() scheme and let also. Maybe on my small memory machine I am running out of memory before things "stabilize"?

I have a question about JS usage. I define objects that wrap matricies and provides various methods. See below. When I perform a "new FrameReal(matrix)" or "new FrameComplex(realMatrix, imagMatrix)" I am worried that the method variable assignments might represent an overhead, because they might be preformed again and again on each new. Is this an issue? Are multiple "function" objects being created for what is in fact a single function in the C++ class sense? Is there a better way to define a heavily allocated C++ class-like object instances that avoids this overhead? I hope my concern here makes sense.

Thanks,
Mike

Code: [Select]
function FrameReal(matrix) {
    this.f1 = function(...) {...};
    this.f2 = function(...) {...};
    this.f3 = function(...) {...};
    ...
}

function FrameComplex(realMatrix, imagMatrix) {
    this.f1 = function(...) {...};
    this.f2 = function(...) {...};
    this.f3 = function(...) {...};
    ...
}

Offline Andres.Pozo

  • PTeam Member
  • PixInsight Padawan
  • ****
  • Posts: 927
Re: PJSR: memory leak?
« Reply #5 on: 2015 January 13 01:14:51 »
I think the problem is that Matrix class allocates large amounts of native memory. The JS engine doesn't know about this and thinks that Matrix objects are small. Since the JS engine doesn't know that it is using too much memory it doesn't launch the GC. When it finally stabilizes it is because the managed memory used by JS objects has reached some threshold that probably depends on the amount of physical memory. I have found similar problems in my job.

The solution of Juan for manually clearing the Matrix objects should fix this problem.

Offline mschuster

  • PTeam Member
  • PixInsight Jedi
  • *****
  • Posts: 1087
Re: PJSR: memory leak?
« Reply #6 on: 2015 January 13 07:42:59 »
Hi Andres,

Your explanation makes sense. I added code to free Image objects, which are similar to Matrix objects in their large native memory usage. The problem is noticeably reduced now. I will work on clearing Matrix objects next.

Thanks,
Mike

Offline mschuster

  • PTeam Member
  • PixInsight Jedi
  • *****
  • Posts: 1087
Re: PJSR: memory leak?
« Reply #7 on: 2015 January 19 16:01:27 »
Hi Juan and Andres,

My script now frees/clears both Image and Matricies. I am pretty sure the code clears them all. The out of memory problem is reduced in severity, but not completely eliminated on my 4GB laptop Win 7 1123. PI out of memory errors still occur after many script executions over long periods of time.

Is PI capable of dumping a list of objects in memory, their types and sizes, to a text file? I would like to see what is in there.

Thanks,
Mike

Offline bitli

  • PTeam Member
  • PixInsight Guru
  • ****
  • Posts: 513
Re: PJSR: memory leak?
« Reply #8 on: 2015 January 21 06:54:03 »
May be this function
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/JS_DumpHeap
could be made available to the script for debugging ?  It may not generate the most readable output, but may provide hint on memory leaks.
-- bitli


Offline mschuster

  • PTeam Member
  • PixInsight Jedi
  • *****
  • Posts: 1087
Re: PJSR: memory leak?
« Reply #9 on: 2015 January 21 11:31:57 »
Hi bitli,

I am thinking that when a script terminates, its heap is null. All JS objects are recovered. All vars, lets, and object property/method assignments and what not are gone.

If I am correct, what remains is outside the JS environment and within the PI core heap. Knowing the contents of the latter would be helpful.

Repeated executions fill memory. So it is the persistent stuff that lives beyond script execution that I want to find.

Thanks,
Mike

Offline mschuster

  • PTeam Member
  • PixInsight Jedi
  • *****
  • Posts: 1087
Re: PJSR: memory leak?
« Reply #10 on: 2015 February 09 22:33:17 »
Should Bitmap be freed/cleared like Matrix and Image for the same reason?

If so, will this do the job?

bitmap.assign(new Bitmap())

Thanks,
Mike

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: PJSR: memory leak?
« Reply #11 on: 2015 February 10 02:39:00 »
Quote
Should Bitmap be freed/cleared like Matrix and Image for the same reason?

Yes, of course. Your code will do the trick for now. I'll add a Bitmap.clear() method (how is it possible that I've overlooked this?) in the next version.
Juan Conejero
PixInsight Development Team
http://pixinsight.com/