Add typemap examples to handle memory management and early garbage collection
git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@7801 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
parent
07d79ce9b2
commit
5b6983d2e1
2 changed files with 508 additions and 1 deletions
|
|
@ -122,6 +122,8 @@
|
|||
<li><a href="#adding_equals_method">Adding an equals method to the Java classes</a>
|
||||
<li><a href="#void_pointers">Void pointers and a common Java base class</a>
|
||||
<li><a href="#struct_pointer_pointer">Struct pointer to pointer</a>
|
||||
<li><a href="#java_memory_management_member_variables">Memory management when returning references to member variables</a>
|
||||
<li><a href="#java_memory_management_objects">Memory management for objects passed to the C++ layer</a>
|
||||
</ul>
|
||||
<li><a href="#java_directors_faq">Living with Java Directors</a>
|
||||
<li><a href="#odds_ends">Odds and ends</a>
|
||||
|
|
@ -2433,7 +2435,8 @@ you're lucky, you will only get a segmentation fault.
|
|||
<p>
|
||||
To work around this, the ownership flag of <tt>o</tt> needs changing to <tt>false</tt>.
|
||||
The ownership flag is a private member variable of the proxy class so this is not possible without some customization of the proxy class.
|
||||
This is achieved using a typemap to add pure Java code to the proxy class and is detailed later in the section on typemaps.
|
||||
This can be achieved by using a typemap to customise the proxy class with pure Java code as detailed later in the section on
|
||||
<a href="#java_typemaps">Java typemaps</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
@ -2491,6 +2494,15 @@ Obj obj = Factory.createObj(); // obj.swigCMemOwn = true;
|
|||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Some memory management issues are quite tricky to fix and may only be noticeable after using for a long time.
|
||||
One such issue is early garbage collection of an object created from Java and resultant usage from C++ code.
|
||||
The section on typemap examples cover two such scenarios,
|
||||
<a href="#java_memory_management_objects">Memory management for objects passed to the C++ layer</a>
|
||||
and
|
||||
<a href="#java_memory_management_member_variables">Memory management when returning references to member variables</a>
|
||||
</p>
|
||||
|
||||
|
||||
<H4><a name="inheritance_mirroring"></a>19.4.3.2 Inheritance</H4>
|
||||
|
||||
|
|
@ -6178,6 +6190,245 @@ The C functional interface has been completely morphed into an object-oriented i
|
|||
the Butler class would behave much like any pure Java class and feel more natural to Java users.
|
||||
</p>
|
||||
|
||||
<H3><a name="java_memory_management_member_variables"></a>19.9.11 Memory management when returning references to member variables</H3>
|
||||
|
||||
|
||||
<p>
|
||||
This example shows how to prevent early garbage collection of objects when the underlying C++ class returns a pointer or reference to a member variable.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Consider the following C++ code:
|
||||
</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
struct Wheel {
|
||||
int size;
|
||||
Wheel(int sz) : size(sz) {}
|
||||
};
|
||||
|
||||
class Bike {
|
||||
Wheel wheel;
|
||||
public:
|
||||
Bike(int val) : wheel(val) {}
|
||||
Wheel& getWheel() { return wheel; }
|
||||
};
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
and the following usage from Java after running the code through SWIG:
|
||||
</p>
|
||||
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
Wheel wheel = new Bike(10).getWheel();
|
||||
System.out.println("wheel size: " + wheel.getSize());
|
||||
// Simulate a garbage collection
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
System.out.println("wheel size: " + wheel.getSize());
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Don't be surprised that if the resulting output gives strange results such as...
|
||||
</p>
|
||||
|
||||
<div class="shell">
|
||||
<pre>
|
||||
wheel size: 10
|
||||
wheel size: 135019664
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
What has happened here is the garbage collector has collected the <tt>Bike</tt> instance as it doesn't think it is needed any more.
|
||||
The proxy instance, <tt>wheel</tt>, contains a reference to memory that was deleted when the <tt>Bike</tt> instance was collected.
|
||||
In order to prevent the garbage collector from collecting the <tt>Bike</tt> instance a reference to the <tt>Bike</tt> must
|
||||
be added to the <tt>wheel</tt> instance. You can do this by adding the reference when the <tt>getWheel()</tt> method
|
||||
is called using the following typemaps.
|
||||
</p>
|
||||
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
%typemap(javacode) Wheel %{
|
||||
// Ensure that the GC doesn't collect any Bike instance set from Java
|
||||
private Bike bikeReference;
|
||||
protected void addReference(Bike bike) {
|
||||
bikeReference = bike;
|
||||
}
|
||||
%}
|
||||
|
||||
// Add a Java reference to prevent early garbage collection and resulting use
|
||||
// of dangling C++ pointer. Intended for methods that return pointers or
|
||||
// references to a member variable.
|
||||
%typemap(javaout) Wheel& getWheel {
|
||||
long cPtr = $jnicall;
|
||||
$javaclassname ret = null;
|
||||
if (cPtr != 0) {
|
||||
ret = new $javaclassname(cPtr, $owner);
|
||||
ret.addReference(this);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
The code in the first typemap gets added to the <tt>Wheel</tt> proxy class.
|
||||
The code in the second typemap constitutes the bulk of the code in the generated <tt>getWheel()</tt> function:
|
||||
</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
public class Wheel {
|
||||
...
|
||||
// Ensure that the GC doesn't collect any bike set from Java
|
||||
private Bike bikeReference;
|
||||
protected void addReference(Bike bike) {
|
||||
bikeReference = bike;
|
||||
}
|
||||
}
|
||||
|
||||
public class Bike {
|
||||
...
|
||||
public Wheel getWheel() {
|
||||
long cPtr = exampleJNI.Bike_getWheel(swigCPtr);
|
||||
Wheel ret = null;
|
||||
if (cPtr != 0) {
|
||||
ret = new Wheel(cPtr, false);
|
||||
ret.addReference(this);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Note the <tt>addReference</tt> call.
|
||||
</p>
|
||||
|
||||
<H3><a name="java_memory_management_objects"></a>19.9.12 Memory management for objects passed to the C++ layer</H3>
|
||||
|
||||
|
||||
<p>
|
||||
Managing memory can be tricky when using C++ and Java proxy classes.
|
||||
The previous example shows one such case and this example looks at memory management for a class passed to a C++ method which expects the object to remain in scope
|
||||
after the function has returned. Consider the following two C++ classes:
|
||||
</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
struct Element {
|
||||
int value;
|
||||
Element(int val) : value(val) {}
|
||||
};
|
||||
class Container {
|
||||
Element* element;
|
||||
public:
|
||||
Container() : element(0) {}
|
||||
void setElement(Element* e) { element = e; }
|
||||
Element* getElement() { return element; }
|
||||
};
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
and usage from C++
|
||||
</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
Container container;
|
||||
Element element(20);
|
||||
container.setElement(&element);
|
||||
cout << "element.value: " << container.getElement()->value << endl;
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
and more or less equivalent usage from Java
|
||||
</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
Container container = new Container();
|
||||
container.setElement(new Element(20));
|
||||
System.out.println("element value: " + container.getElement().getValue());
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
The C++ code will always print out 20, but the value printed out may not be this in the Java equivalent code.
|
||||
In order to understand why, consider a garbage collection occuring...
|
||||
</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
Container container = new Container();
|
||||
container.setElement(new Element(20));
|
||||
// Simulate a garbage collection
|
||||
System.gc();
|
||||
System.runFinalization();
|
||||
System.out.println("element value: " + container.getElement().getValue());
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
The temporary element created with <tt>new Element(20)</tt> could get garbage collected
|
||||
which ultimately means the <tt>container</tt> variable is holding a dangling pointer, thereby printing out any old random value instead of the expected value of 20.
|
||||
One solution is to add in the appropriate references in the Java layer...
|
||||
</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
public class Container {
|
||||
|
||||
...
|
||||
|
||||
// Ensure that the GC doesn't collect any Element set from Java
|
||||
// as the underlying C++ class stores a shallow copy
|
||||
private Element elementReference;
|
||||
private long getCPtrAndAddReference(Element element) {
|
||||
elementReference = element;
|
||||
return Element.getCPtr(element);
|
||||
}
|
||||
|
||||
public void setElement(Element e) {
|
||||
exampleJNI.Container_setElement(swigCPtr, getCPtrAndAddReference(e));
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
The following typemaps will generate the desired code.
|
||||
The 'javain' typemap matches the input parameter type for the <tt>setElement</tt> method.
|
||||
The 'javacode' typemap simply adds in the specified code into the Java proxy class.
|
||||
</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
%typemap(javain) Element *e "getCPtrAndAddReference($javainput)"
|
||||
|
||||
%typemap(javacode) Container %{
|
||||
// Ensure that the GC doesn't collect any element set from Java
|
||||
// as the underlying C++ class stores a shallow copy
|
||||
private Element elementReference;
|
||||
private long getCPtrAndAddReference(Element element) {
|
||||
elementReference = element;
|
||||
return Element.getCPtr(element);
|
||||
}
|
||||
%}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<H2><a name="java_directors_faq"></a>19.10 Living with Java Directors</H2>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue