Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nasty Memory Leak #2

Closed
jocopa3 opened this issue Jan 24, 2017 · 1 comment
Closed

Nasty Memory Leak #2

jocopa3 opened this issue Jan 24, 2017 · 1 comment

Comments

@jocopa3
Copy link

jocopa3 commented Jan 24, 2017

I know this is an old project with little activity, but I'm leaving this issue here as a warning to future users. There is a massive memory leak in this wrapper.

Basic Info

I'm using Mojang's LevelDB library, which is based on the latest version of LevelDB 1.18, but supports Windows and adds zlib compression. On both Windows and Ubuntu 14.04 (only two platforms I have to test with), I encounter a very large memory leak where the number of objects allocated outside the Java heap continues to grow until eventually the program uses up all system RAM. I spent 2 days looking into this issue until I finally figured out it was due to my complete ignorance of what this JNA wrapper does behind the scenes.

The Problem

LevelDB.get is the main cause of the memory leak, though there may be smaller leaks littered throughout the wrapper. The native LevelDB library returns an malloc'd byte array to the wrapper, which means it's up to the wrapper to free the byte array. However, this wrapper's LevelDB.get method returns a copy of the native byte array allocated by the LevelDB library, but doesn't free the original native byte array. This means the original byte array malloc'd by the native LevelDB library is never freed and persists outside the Java heap, completely invisible to the Java garbage collector.

JNA will not automatically free the byte arrays returned by the native LevelDB library or pointed to by a PointerToReference. These arrays must be freed manually.

Solutions

For future users, fork this repo and change LevelDB.get using one of the following solutions:


Solution 1

Modify the LevelDB.get function to clear the data being pointed to before returning the copied byte array, like so:

byte[] returnValue = result != null ? result.getPointer().getByteArray(0, (int) resultLength) : null;
if(result != null)
    LevelDBNative.leveldb_free(result.getPointer());
return returnValue;

This option also has lots of overhead as Java still has to copy the original byte array to the Java heap.


Solution 2

Modify the LevelDB.get function to return a ByteBuffer mapped to the original byte data using:

result.getPointer().getByteBuffer(0, (int) resultLength)

This option will have a little less overhead as Java doesn't have to copy the original byte array; however, the ByteBuffer returned is a DirectByteBuffer, which may not be ideal for some applications and use-case scenarios.

@jocopa3 jocopa3 changed the title Easy Potential for a Memory Leak Nasty Memory Leak Jan 25, 2017
@maxd maxd closed this as completed in f62338e Jan 25, 2017
@maxd
Copy link
Contributor

maxd commented Jan 25, 2017

@jocopa3 Thank you a lot! You have found a serious problem and clearly describe it. Excellent job!

I have chosen the first solution to fix because it allows to save old interface.

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

No branches or pull requests

2 participants