JOGL Leaking Native Memory: Win & Mac, but not

JOGL has been working great as a cross-platform way of generating OpenGL contexts for handing off to other native modules.

However Jogl (1.1b07) seems to be leaking about 0.5 or 1.5 MB of native memory for each GLCanvas that is created, used, and destroyed on Win32 and Mac respectively. Progress can be tracked using “ps” on the Mac and the task manager process list on Windows. Eventually the process runs out of memory and dies. The Linux implementation doesn’t has this problem…memory usage is constant and the program runs indefinitely.

I’ve built JOGL on Win32 and activated the DEBUG code to confirm that the destroy methods are being called (destroy & destroyImpl) as expected when the GLCanvases are removed from their containers. Runtime.getRuntime().totalMemory() and Runtime.getRuntime().freeMemory() also don’t grow, hence the basis for my suspicion that itis native memory that’s leaking somewhere in the platform-specific implementations…

Before I go through the trouble of boiling this down to a simple test case for a bug report, is anyone already familar with this issue and know of a solution? Or, is this the expected behavior with no workaround likely?

Here is an update on the Windows situation:

Every call to WGL.SetPixelFormat (WindowsGLContext) seems to chew up a significant chunk of RAM (0.5 MB) that isn’t released until the JVM quits, even if the window is destroyed.

However, it also seems necessary to call WGL.SetPixelFormat for every new HDC…one for each new GLCanvas. As a result, creating and destroying numerous GLCanvases will exhaust the process memory.

So what’s the cause of the leak?

According to Microsoft: “If hdc references a window, calling the SetPixelFormat function also changes the pixel format of the window. Setting the pixel format of a window more than once can lead to significant complications for the Window Manager and for multithread applications, so it is not allowed. An application can only set the pixel format of a window one time. Once a window’s pixel format is set, it cannot be changed.”

Hmm. Do these hdc’s reference a window or not? Alternatively, I wonder if JAWT is calling ReleaseDC or DeleteDC when it is needed? Perhaps that could explain the leak…

I’ve seen what appear to be resource leaks on Windows and Linux, actually, using the TestContextDestruction demo in the jogl-demos workspace. That app seems to always slow down significantly after removing and re-adding the GLCanvas object many times. Up to this point I haven’t been able to figure out where the problem is coming from.

The HDC returned from the JAWT does (or should) reference a window. The JAWT and AWT should be destroying the DC for this window correctly. If you are seeing leaks on the Mac then it sounds like there may be a deeper problem than just a Windows-specific bug. It would be great if you could create a test case and file a bug.

Issue 125 posted to

https://jogl.dev.java.net/issues/show_bug.cgi?id=125

along with a 1-page working example.

Here is another interesting observation: If you create a pure windows application that has:

  1. a parent window (a JFrame mimic)
  2. and a child window (a Canvas mimic) inside that parent.

If you SetPixelFormat and then create a wglContext in the child window, then you basically have an analog to JOGL’s GLCanvas. So now let’s experiment…

A) If you destroy the wglContext and the child window and then recreate them over and over again, then there is no memory leak. Great! This isn’t a fundamental Windows bug…

B) However, if you destroy the wglContext but neglect to destroy the child, then you end up with a memory leak of about 0.6 MB per Context – exactly matching the behavior seen with JOGL.

So… circumstantial evidence suggests that AWT may not actually be destroying the underlying Window that represents the Canvas used by GLCanvas. The call to super.removeNotify() in GLCanvas.removeNotify() is supposed to do this…and isDisplayable indeed returns false after super.removeNotify is called. But apparently the child window isn’t actually destroyed, or there is something else amiss with the configuration of that child window or its device context that is preventing release of those resources.

Hmm…

Okay, here’s my theory: there is something incompatible between the way Java AWT creates child windows and the way OpenGL needs them to be configured to permit clean resource handling. Here is my most favored scenario:

JAWT may create child windows with the CS_PARENTDC or CS_CLASSDC window class attributes instead of CS_OWNDC for performance reasons. Based on the documentation for SetPixelFormat, it seems likely that device context resources wouldn’t be released when the child window is destroyed if CS_OWNDC was not used.

One potential fix or workaround would be to create a separate native child window of the canvas with its own DC to hold the wglContext and its associated pixel format. That approach would insure that the resources get freed up when the wglContext is no longer needed.

As far as I can tell from looking at the Windows AWT sources, no style at all is specified for the window class for AWT Components. The MSDN documentation says that in this case the DC for the window is retrieved from a pool and that it’s supposed to be re-fetched and re-initialized each time painting occurs. I’ve looked at the code which implements some of the JAWT operations and DCX_CACHE is being used in the GetDCEx call which provides the DC. At some level it looks to me like we’re just lucky if we get the same device context from call to call, although the JAWT does provide information about whether the underlying HWND changed since the last render. It’s a little scary to think about this happening. I’m not 100% convinced of this diagnosis (yet). I’ll try to find out some more information from the AWT team.

It does seem that in order to fix this problem we should be creating our own window class with CS_OWNDC and putting that inside of the Canvas’s HWND. However due to Windows’ apartment threading model I think that there could be significant problems with AWT interoperability. We should track this issue but I’m not sure we can resolve it quickly. Thanks for filing the issue about it.

Have you looked into the resource leaks on the Mac? I would think that things should be much cleaner on that platform.

The AWT sources aren’t open are they?

I haven’t messed around much with the Mac because the company that’s funding this development is mostly Linux and Windows. The absense of a memory leak on Linux suggests that the problem is platform-specific, but one never knows for sure.

From the MS docs, it sounds like the DC used by JOGL needs to be specifically associated with the window, since a DC pulled from the common pool isn’t guaranteed to draw with the same pixel format (which is the technical requirement for the use of the DC with the windows OpenGL Context – it’s doesn’t have to be the same exact DC as was previously used, so long as it has the same pixel format).

The J2SE sources are all available under the Sun Community Source License. Search java.sun.com for “scsl 1.4.2” or go to http://wwws.sun.com/software/communitysource/j2se/java2/index.html .

I can’t reproduce the memory leak on my Windows XP machine with an NVidia Quadro FX Go700 chipset and fairly recent drivers. What kind of graphics card do you have? Does specifying -Dsun.java2d.noddraw=true on the command line change the behavior?

It does look like the DC needs to remain constant. Looking more at the JAWT sources it looks like the DC will not change unless the underlying window changes, which makes sense. However the entire cleanup process is still not 100% clear to me. I was planning to work with one of the AWT engineers to try specifying CS_OWNDC inside the AWT implementation but I now think this might just be a driver bug rather than a bug in how JOGL and the AWT interact.

[quote]JOGL has been working great as a cross-platform way of generating OpenGL contexts for handing off to other native modules.

However Jogl (1.1b07) seems to be leaking about 0.5 or 1.5 MB of native memory for each GLCanvas that is created, used, and destroyed on Win32 and Mac respectively. Progress can be tracked using “ps” on the Mac and the task manager process list on Windows. Eventually the process runs out of memory and dies. The Linux implementation doesn’t has this problem…memory usage is constant and the program runs indefinitely.

I’ve built JOGL on Win32 and activated the DEBUG code to confirm that the destroy methods are being called (destroy & destroyImpl) as expected when the GLCanvases are removed from their containers. Runtime.getRuntime().totalMemory() and Runtime.getRuntime().freeMemory() also don’t grow, hence the basis for my suspicion that itis native memory that’s leaking somewhere in the platform-specific implementations…

Before I go through the trouble of boiling this down to a simple test case for a bug report, is anyone already familar with this issue and know of a solution? Or, is this the expected behavior with no workaround likely?
[/quote]
Are you using ATI hardware?

I know that there was a major memory leak in ATI drivers for every created context. For Mac OS X, it is supposed to be fixed for Tiger (Mac OS X 10.4).

cheers