Skip to content
This repository has been archived by the owner on Jan 1, 2020. It is now read-only.

Memory Leak Issue #23

Closed
epochrion opened this issue Oct 11, 2017 · 6 comments
Closed

Memory Leak Issue #23

epochrion opened this issue Oct 11, 2017 · 6 comments

Comments

@epochrion
Copy link
Contributor

epochrion commented Oct 11, 2017

I have run into an issue related to a memory leak. Effectively, after having disposed of a mesh (along with any linked geometry, texture, or material) and after ensuring that there are no longer any references to the mesh, the allocated space in memory is never freed when the garbage collector runs. Consequently, the application crashes should sufficient meshes have been created and later disposed.

For reference, I have uploaded sample code that illustrates this issue. The linked example creates a mesh using an image texture every 60 frames. The mesh is then deleted 30 frames after it is created. This process repeats indefinitely. If a memory profiler is attached to the process, it can be noticed that the allocated memory always continues to increase rather than fluctuating and stabilizing around a central value as may be expected. Find linked an image from a memory profiler that was attached to the above example code.

More details related to the testing that was conducted are listed below:

Testing Scenario Significant Memory Leak Present?
1 🔵 No
2 🔵 No
3 🔴 Yes
4 🔴 Yes
5 🔵 No

The testing scenarios as numbered in the table above are:

  1. Empty React Native Application, without react-native-webgl or Three JS
  2. React Native Application, with PanResponder, and an empty Three JS scene
  3. React Native Application, as per the example code above, but never calling disposeImage()
  4. React Native Application, exactly as per the example code above, and calling disposeImage()
  5. HTML Web Page, using only Three JS, with almost the exact same code as the above example

I believe the issue is related to the react-native-webgl library, and it is not related to Three JS due to scenario 5 above. With the exception of how the image itself is loaded due to a difference in target platform, the rest of the code in scenario 5 is almost the same as in scenario 4. Yet, the memory leak example is present in scenario 4 and not in scenario 5. Find linked sample code for scenario 5.

It is also interesting to note that the amount of memory allocated after 1 minute 30 seconds is approximately 60-65 MB in scenario 3, and is approximately 95-100 MB in scenario 4. There appears to be a noticeable increase in the memory leak when scene.remove(mesh) is called versus when it is not called.

From the testing conducted, I wonder if the react-native-webgl library is keeping a reference to the created texture or mesh somewhere, and hence preventing the allocated memory from being freed?

@emilebres
Copy link

Looking at your code, it looks like you are not unloading the texture in your disposeImage function.

Try calling gl.getExtension("RN").unloadTexture(texture) where texture is the object return by gl.getExtension("RN").loadTexture.

@epochrion
Copy link
Contributor Author

Interesting, thank you for catching that. I ran a few more quick tests, and I'll summarize the results below:

  1. Invoking the unloadTexture function before the dispose function (provided by Three JS) reduces the memory leak by approximately 10 MB per minute, though the issue is still present.
glex.unloadTexture(mesh.material.map);
mesh.material.map.dispose();
mesh.material.map = undefined;
  1. Invoking the unloadTexture function after the dispose function (provided by Three JS) has no affect on the rate of the memory leak, and the issue is still present. I think this result is expected, and it is simply mentioned here for documentation purposes in case anyone else has code set up like this.
mesh.material.map.dispose();
glex.unloadTexture(mesh.material.map);
mesh.material.map = undefined;
  1. It appears that the solution to this issue is to invoke the unloadTexture function exactly as @emilebres commented: on the texture object returned by the loadTexture function. Thus, the final form of the function that should ensure that garbage collection will run smoothly is as shown below:
function disposeMesh(mesh) {
    scene.remove(mesh);
    mesh.geometry.dispose();
    mesh.geometry = undefined;
    let properties = renderer.properties.get(mesh.material.map);
    glex.unloadTexture(properties.__webglTexture);
    mesh.material.map.dispose();
    mesh.material.map = undefined;
    mesh.material.dispose();
    mesh.material = undefined;
    mesh = undefined;
}

Running the sample code that I provided earlier, the memory used by the application remains stable between 7 MB - 13 MB. I hope this helps anyone else who encounters such an issue in the future. I will run more comprehensive tests shortly, and I will provide an update if the issue re-appears.

As an aside note: could the documentation associated with the unloadTexture function please be revised to explicitly state that the function needs to be invoked when removing textures? I suppose I was under the impression that the unloadTexture function is available in order to facilitate the removal of a texture from a scene (similar to scene.remove in Three JS) rather than a mechanism to ensure the proper disposal of a texture from memory. A simple misunderstanding, but one with dramatic effects ;)

@gre
Copy link
Collaborator

gre commented Oct 27, 2017

feel free to send PR to clarify the doc, but yeah basically any code doing t = rngl.loadTexture() should have the dual rngl.unloadTexture(t)

@gre
Copy link
Collaborator

gre commented Oct 27, 2017

if you destroy the gl context (aka unmount the view), I think you won't have to do that, but if you accumulate textures that you no longer use, you should probably call it to not leak memory

@epochrion
Copy link
Contributor Author

Pull request has been submitted. Thank you for your help. Once again, I'll run some more extensive tests today and early next week, and if I notice that the issue still exists, I'll provide an update. If the issue still appears to be fixed once the testing is complete, I'll let you know and the issue can then be closed.

@epochrion
Copy link
Contributor Author

Conducted some more tests, primarily on an emulated Android device with a memory profiler attached. Looks like the memory usage remains stable, This issue can be closed.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants