Add documentation patch #1743573 from Jeffrey Sorensen about better memory management by the JVM of C++ allocated memory
git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@9894 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
parent
3f0bbe1581
commit
6b412b1e86
2 changed files with 122 additions and 0 deletions
|
|
@ -98,6 +98,7 @@
|
|||
<li><a href="#simple_pointers">Simple pointers</a>
|
||||
<li><a href="#c_arrays">Wrapping C arrays with Java arrays</a>
|
||||
<li><a href="#unbounded_c_arrays">Unbounded C Arrays</a>
|
||||
<li><a href="#java_heap_allocations">Overriding new and delete to allocate from Java heap</a>
|
||||
</ul>
|
||||
<li><a href="#java_typemaps">Java typemaps</a>
|
||||
<ul>
|
||||
|
|
@ -4143,6 +4144,123 @@ well suited for applications in which you need to create buffers,
|
|||
package binary data, etc.
|
||||
</p>
|
||||
|
||||
<H3><a name="java_heap_allocations"></a>20.7.5 Overriding new and delete to allocate from Java heap</H3>
|
||||
|
||||
|
||||
<p>
|
||||
Unlike some languages supported by SWIG, Java has a true garbage collection
|
||||
subsystem. Other languages will free SWIG wrapped objects when their reference
|
||||
count reaches zero. Java only schedules these objects for finalization, which
|
||||
may not occur for some time. Because SWIG objects are allocated on the C
|
||||
heap, Java users may find the JVM memory use
|
||||
quickly exceeds the assigned limits, as memory fills with unfinalized proxy
|
||||
objects. Forcing garbage collection is clearly an undesirable solution.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
An elegant fix for C++ users is to override new and delete using the following
|
||||
code (here shown included in a SWIG interface file)
|
||||
</p>
|
||||
|
||||
<div class="code">
|
||||
<pre>
|
||||
/* File: java_heap.i */
|
||||
%module test
|
||||
%{
|
||||
#include <stdexcept>
|
||||
#include "jni.h"
|
||||
|
||||
/**
|
||||
* A stash area embedded in each allocation to hold java handles
|
||||
*/
|
||||
struct Jalloc {
|
||||
jbyteArray jba;
|
||||
jobject ref;
|
||||
};
|
||||
|
||||
static JavaVM *cached_jvm = 0;
|
||||
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||
cached_jvm = jvm;
|
||||
return JNI_VERSION_1_2;
|
||||
}
|
||||
|
||||
static JNIEnv * JNU_GetEnv() {
|
||||
JNIEnv *env;
|
||||
jint rc = cached_jvm->GetEnv((void **)&env, JNI_VERSION_1_2);
|
||||
if (rc == JNI_EDETACHED)
|
||||
throw std::runtime_error("current thread not attached");
|
||||
if (rc == JNI_EVERSION)
|
||||
throw std::runtime_error("jni version not supported");
|
||||
return env;
|
||||
}
|
||||
|
||||
void * operator new(size_t t) {
|
||||
if (cached_jvm != 0) {
|
||||
JNIEnv *env = JNU_GetEnv();
|
||||
jbyteArray jba = env->NewByteArray((int) t + sizeof(Jalloc));
|
||||
if (env->ExceptionOccurred())
|
||||
throw bad_alloc();
|
||||
void *jbuffer = static_cast<void *>(env->GetByteArrayElements(jba, 0));
|
||||
if (env->ExceptionOccurred())
|
||||
throw bad_alloc();
|
||||
Jalloc *pJalloc = static_cast<Jalloc *>(jbuffer);
|
||||
pJalloc->jba = jba;
|
||||
/* Assign a global reference so byte array will persist until delete'ed */
|
||||
pJalloc->ref = env->NewGlobalRef(jba);
|
||||
if (env->ExceptionOccurred())
|
||||
throw bad_alloc();
|
||||
return static_cast<void *>(static_cast<char *>(jbuffer) + sizeof(Jalloc));
|
||||
}
|
||||
else { /* JNI_OnLoad not called, use malloc and mark as special */
|
||||
Jalloc *pJalloc = static_cast<Jalloc *>(malloc((int) t + sizeof(Jalloc)));
|
||||
if (!pJalloc)
|
||||
throw bad_alloc();
|
||||
pJalloc->ref = 0;
|
||||
return static_cast<void *>(
|
||||
static_cast<char *>(static_cast<void *>(pJalloc)) + sizeof(Jalloc));
|
||||
}
|
||||
}
|
||||
|
||||
void operator delete(void *v) {
|
||||
if (v != 0) {
|
||||
void *buffer = static_cast<void *>( static_cast<char *>(v) - sizeof(Jalloc));
|
||||
Jalloc *pJalloc = static_cast<Jalloc *>(buffer);
|
||||
if (pJalloc->ref) {
|
||||
JNIEnv *env = JNU_GetEnv();
|
||||
env->DeleteGlobalRef(pJalloc->ref);
|
||||
env->ReleaseByteArrayElements(pJalloc->jba, static_cast<jbyte *>(buffer), 0);
|
||||
}
|
||||
else {
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
%}
|
||||
...
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
This code caches the Java environment during initialization,
|
||||
and when new is called, a Java ByteArray is allocated to provide the
|
||||
SWIG objects with space in the Java heap. This has the combined
|
||||
effect of re-asserting the Java virtual machine's limit on memory allocation,
|
||||
and puts additional pressure on the garbage collection system to run more
|
||||
frequently.
|
||||
This code is made slightly more complicated because allowances must be made
|
||||
if new is called before the JNI_OnLoad is executed. This can happen during
|
||||
static class initialization, for example.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Unfortunately, because most Java implementations call malloc and free, this
|
||||
solution will not work for C wrapped structures. However, you are free to
|
||||
make functions that allocate and free memory from the Java heap using this
|
||||
model and use these functions in place of malloc and free in your own
|
||||
code.
|
||||
</p>
|
||||
|
||||
<H2><a name="java_typemaps"></a>20.8 Java typemaps</H2>
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue