7

Given this C++ Code:

void LoadData(char** myVar)
{
    std:: string str("[Really Long String Here]");
    unsigned int size = str.length() + 1;
    *myVar = new char[size];
    strncpy(*myVar, str.c_str(), size);
}

And this JNA Java:

Pointer myVar = new Memory(Pointer.SIZE);
this.Lib.LoadData(myVar);
this.someVar = myVar.getPointer(0).getString(0);

I'm having memory leaks, as I understand it, getPointer(0) should create a pointer object that should be released on finalize(), but it seems to not be.

Am I missing something? This seems up to spec... and I can run the function above with no leaks in C++ fine.

I call the Java code in a loop to test the leak, I've tried putting in pauses, and manually calling the GC, also it'll bloat to gigabytes rather quickly this way.

I've been banging my head against this for a few days now and it sucks to get hung up on something so trivial as attempting to free memory.As far as I can tell I can only manually free memory in Java if I have the address, but I can't see how I'd get that.

Edit:

Nevermind, I don't even think there is a way to do manually free through JNA without extending it...

StrangeWill
  • 2,106
  • 1
  • 23
  • 36

3 Answers3

4

Add this function to the C++ library...

void FreeData(char** myVar)
{
    delete [] *myVar;
}

And then make this the JNA code

Pointer myVar = new Memory(Pointer.SIZE);
this.Lib.LoadData(myVar);
this.someVar = myVar.getPointer(0).getString(0);
this.Lib.FreeData(myVar);

This way you allocate and delete the memory in C++.

PatriotBob
  • 123
  • 4
  • This so far seems to work, but is there really no other way in JNA to free data? :( – StrangeWill Mar 05 '12 at 21:26
  • 1
    JNA is just a wrapper to a native library. It doesn't manage native memory especially direct native buffer. It's the task of the native library designer to provide interfaces to allocate/deallocate the memory the library uses. – ecle Mar 05 '12 at 22:37
  • 1
    JNA does have internal methods for freeing memory, just odd that I'd have to implement it again because it's all protected in JNA. – StrangeWill Mar 05 '12 at 23:27
  • @StrangeWill Maybe it is because its reference is not released for GC to take over http://stackoverflow.com/questions/1854398/how-to-garbage-collect-a-direct-buffer-java – ecle Mar 06 '12 at 12:38
  • It is just the same like allocating a memory from a heap using Windows Kernel32's `HeapAlloc()`, we need to deallocate it using `HeapFree()`. If we have a memory allocation function in C, it must be accompanied with a memory deallocation function as well. – ecle Mar 06 '12 at 12:55
  • JNA can free arbitrary blocks of memory allocated with "malloc" (Native.free), but the C++ delete[] operator is a different beast (technically "delete" is as well, since it may or may not simply use malloc-allocated memory). – technomage Mar 06 '12 at 17:06
  • PointerByReference is preferred over "new Memory(Pointer.SIZE)", since the former makes your intentions explicit. – technomage Mar 06 '12 at 17:08
1

Allocate in the caller, not the callee.

For example:

int LoadData(char* buf, int maxlen) {
    std:: string str("[Really Long String Here]");
    strncpy(buf, str.c_str(), maxlen);
    if (str.length() < maxlen) 
        return str.length();
    return maxlen;
}

Then when you call from Java, pass in a byte[] of the appropriate size. Note that this implementation is potentially very inefficient, but the idea is that you don't generally want to be allocating memory in one context and deallocating it in another.

technomage
  • 9,861
  • 2
  • 26
  • 40
  • 1
    In implementation: char* buf can be 10 bytes, 10k or 150k, possibly more once I implement the full code. Other than just allocating a few megs and just putting up with the inefficient implementation (my software can, but I'm more interested in the _right_ way to do this with JNA). – StrangeWill Mar 05 '12 at 18:35
  • If you want the callee to manage the storage, you can return a "const char *"; JNA will copy out the contents into a Java String, after which you are safe to modify the buffer contents and/or delete it. If you return Pointer, you can avoid the memory copy until the Java side actually needs to look at the data. The "right" way to do it depends highly on which parts (java or native) of your program need to access the data, how often, and how long the buffer will live. – technomage Mar 06 '12 at 17:04
0

Instead of myVar = new char[size]

use

*myVar = malloc(size);
strncpy(*myVar, str.c_str(), size);

Arrays need to be deleted like: delete [] *myVar;

JNA prolly doesn't know to do that.

Todd Murray
  • 423
  • 2
  • 7