Reduce runtime memory use#699
Conversation
Reduces memory use by watershed (release temp objects and reuse memory) Schedule GC to run every 15 seconds I can run in headless mode with -Xmx15m and UI mode with -Xmx150m (was previously seeing up to 890MB heap in UI with watershed)
Current coverage is 53.54% (diff: 20.00%)@@ master #699 diff @@
==========================================
Files 225 226 +1
Lines 7329 7364 +35
Methods 0 0
Messages 0 0
Branches 721 724 +3
==========================================
+ Hits 3934 3943 +9
- Misses 3221 3247 +26
Partials 174 174
|
Sets -Xmx200m in native app Also fixes watershed output
dc07b19 to
79d5c86
Compare
|
So just reducing the max heap size will cause more garbage collections on the young generation, but doesn't seem to run full GC's any more often. The biggest problem with memory is that if the GC leaves dead native objects (Mats, Rects, etc.) floating around, the native memory won't get freed up and causes the process's memory use to explode -- 290MB of used heap space can correspond to 10GB of memory used by the process (this could go even higher, but my 16GB of RAM was used up and swapping was getting nasty). This was just running the core jar. Calling Calling Calling Heap and CPU profiles
|
Currently set to run after every 15 pipeline runs
|
I'd like to get some other people's thoughts on this before we merge it. @saudet Is calling |
|
|
|
deallocate() should also free memory immediately, it doesn't?
To free the Pointer object itself from the Java heap, we need to wait after
the GC though...
|
|
So it looks like the only way to address this is to periodically call The first one has the advantage of not putting any limits on the heap size, but it will trigger a major stop-the-world GC, which can take a while. It also keeps memory use very low in both UI and headless modes. The second option will only trigger minor GCs in Eden and S1/S2 that don't take much time, but that will impose limits on other parts of the program. For example, previewing large images in the UI takes a ton of memory on the heap -- a 12MP picture can take upwards of 150MB to preview -- and won't be able to fit in new gen, making the app hang while the JVM keeps trying to unsuccessfully allocate enough memory for it. #711 addresses this problem, but there may be other parts of the program or future features that would be limited by a tiny new gen space. I'm not happy with either of these options, but I don't see any other alternatives. |
|
For cases like these, it's useful to reuse (pre)allocated memory as much as possible, to avoid constantly allocating and deallocating. I am not familiar with the details of this project, but that doesn't look possible? |
|
I don't think it's possible for us to use preallocated memory. We also can't make eden small because that puts limits on the size of the images we can preview in the UI (big images = big buffers that need to be allocated). So the only solution I see is to periodically call |
|
@JLLeitschuh I'd like you to try this out and see if you can find any serious problems |
|
@SamCarlberg BTW, recent versions of JavaCPP check before each allocation the amount of physical memory used and call |
|
That's good to know. I'll try it out, see how it compares to running |
| CvMat mat = cvMat(1, b.length, CV_8UC1, new BytePointer(b)); | ||
| if (decoded != null) { | ||
| cvReleaseImage(decoded); | ||
| } |
| private final InputSocket<ContoursReport> contoursSocket; | ||
| private final OutputSocket<ContoursReport> outputSocket; | ||
|
|
||
| private static final int maxMarkers = 253; |
There was a problem hiding this comment.
Shouldn't this be MAX_MARKERS?
| private final OutputSocket<ContoursReport> outputSocket; | ||
|
|
||
| private static final int maxMarkers = 253; | ||
| private final List<Mat> markerPool = new ArrayList<>(maxMarkers); |
There was a problem hiding this comment.
Can this be an immutable list?
| runsSinceLastGc = 0; | ||
| lastRun = System.nanoTime(); | ||
| System.gc(); | ||
| } |
| for (int i = 0; i < MAX_MARKERS; i++) { | ||
| tmpPool.add(new Mat()); | ||
| } | ||
| markerPool = ImmutableList.copyOf(tmpPool); |
There was a problem hiding this comment.
Use the ImmutableListBuilder
There was a problem hiding this comment.
That's no better than what's currently happening.
There was a problem hiding this comment.
If we use Guava 21.0:
markerPool = Stream.generate(Mat::new).limit(MAX_MARKERS).collect(ImmutableList.toImmutableList());
| /** | ||
| * The maximum number of runs allowed before calling System.gc(). | ||
| */ | ||
| private static final int MAX_RUNS_BEFORE_GC = 5; |
There was a problem hiding this comment.
Should this be called minimum instead?
There was a problem hiding this comment.
Yeah, I should change that
| for (int i = 0; i < MAX_MARKERS; i++) { | ||
| tmpPool.add(new Mat()); | ||
| } | ||
| markerPool = ImmutableList.copyOf(tmpPool); |
There was a problem hiding this comment.
If we use Guava 21.0:
markerPool = Stream.generate(Mat::new).limit(MAX_MARKERS).collect(ImmutableList.toImmutableList());





Reduces memory use by watershed (release temp objects and reuse memory)
I can run in headless mode with -Xmx15m and UI mode with -Xmx150m (was previously seeing up to 890MB heap in UI with watershed)