Closed nasamaher closed 3 years ago
Here is a runnable test case.
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import hageldave.jplotter.canvas.BlankCanvas;
import hageldave.jplotter.renderables.Lines;
import hageldave.jplotter.renderers.CoordSysRenderer;
import hageldave.jplotter.renderers.LinesRenderer;
public class JPlotterLeak extends JPanel
{
private final BlankCanvas fCanvas;
private final CoordSysRenderer fCoordsys;
private final double[] fIndices = new double[1_000_000];
public JPlotterLeak()
{
for (int i = 0; i < fIndices.length; i++)
{
fIndices[i] = i;
}
fCoordsys = new CoordSysRenderer();
fCanvas = new BlankCanvas().setRenderer(fCoordsys);
fCanvas.setPreferredSize(new Dimension(250, 250));
add(fCanvas);
}
private void processNewResults()
{
Lines lines = new Lines();
lines.addLineStrip(fIndices, fIndices);
LinesRenderer linesRenderer = new LinesRenderer();
linesRenderer.addItemToRender(lines);
// Also tried reusing CoordSysRenderer
CoordSysRenderer coordsys = new CoordSysRenderer();
fCanvas.setRenderer(coordsys);
coordsys.setCoordinateView(0, 0, fIndices.length, fIndices.length);
coordsys.setContent(linesRenderer);
fCanvas.repaint();
lines.close();
linesRenderer.close();
coordsys.close();
}
public static void main(String[] args)
{
JPlotterLeak p = new JPlotterLeak();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(p);
frame.pack();
frame.setVisible(true);
new Thread(new Runnable()
{
@Override
public void run()
{
while (true)
{
p.processNewResults();
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}).start();
}
}
Thanks I'm looking into this.
Okay, so you encountered a nasty detail of JPlotter / LWJGL concerning native resource management. In order for your code to not leak, you need to run the close() statements when the respective OpenGL context is present. The GL context is not always present, usually only when AWTGLCanvas
(BlankCanvas
) renders.
The close operations are not working, i.e. not releasing GL resources since the context is not present. In fact, any method of JPlotter that is annotated with @GLContextRequired
is not working correctly when executed without active GL context.
/**
* This annotation serves as a label to signalize developers that the
* annotated method is to be called in a thread that currently owns an
* OpenGL context.
* Such context is present for example during
* {@link AWTGLCanvas#render()},
* {@link AWTGLCanvas#runInContext(Runnable)} or
* {@link AWTGLCanvas#executeInContext(java.util.concurrent.Callable)}.
* Of course if the calling thread does not own a GL context the latter
* two methods may be used if possible. It is recommended though
* to avoid this due to the overhead required to obtain a GL context.
*
* @author hageldave
*/
@Documented
@Target({METHOD,CONSTRUCTOR})
public @interface GLContextRequired {}
This means in your case, that you need to execute the close statements like this for example:
fCanvas.runInContext(()->{
lines.close();
linesRenderer.close();
coordsys.close();
});
This is pretty ugly though and I would not recommend this. From your use case it seems that you need to update the displayed line over and over again. Instead of creating a new CoordSysRenderer
, LinesRenderer
, and Lines
object on every update, you could reuse all of these objects, leave coordsys and linesrenderer untouched, and simply clear your Lines
object. This way you don't need to create new objects and also don't need to call close.
lines.removeAllSegments();
lines.addLineStrip(fIndices,fIndices);
Have a look at the IsolinesViz example, I think it has a similar use case. It needs to update a line according to the current mouse location during a mouse drag. This happens several times per second.
https://github.com/hageldave/JPlotter/blob/17ceb43a6238a41742179045790094516fdb9608/jplotter/src/test/java/hageldave/jplotter/IsolinesViz.java#L127
David,
Awesome. I should have paid closer attention to your thorough documentation. Reusing Lines instance works perfectly. Thanks for looking into this.
Steve
Version: 0.4.0 OS: Windows 10
Hi, I'm exploring JPlotter for use in a high-speed "oscilloscope" display and am pushing +1M line segments per second to a CoordSysRenderer - while disposing the previous segments. I can't seem to stop runaway (native) memory leaks outside the JVM. My 2G JVM stays happy but actual memory usage flies to 8G ~within a minute. It's in-use memory as I froze my 32GB machine during early tests.
Below is my (most extreme) attempt to clean up resources by locally allocating and closing as much as I can.
Thanks,
Steve