Author: mladen.turk(a)jboss.com
Date: 2007-10-11 02:57:32 -0400 (Thu, 11 Oct 2007)
New Revision: 1094
Modified:
trunk/sight/java/org/jboss/sight/File.java
trunk/sight/java/org/jboss/sight/NativeObject.java
trunk/sight/native/include/sight_local.h
trunk/sight/native/include/sight_types.h
trunk/sight/native/share/library.c
trunk/sight/native/share/no.c
trunk/sight/test/org/jboss/sight/MemoryLeakTest.java
Log:
Fix NativeObject GC
Modified: trunk/sight/java/org/jboss/sight/File.java
===================================================================
--- trunk/sight/java/org/jboss/sight/File.java 2007-10-10 06:28:46 UTC (rev 1093)
+++ trunk/sight/java/org/jboss/sight/File.java 2007-10-11 06:57:32 UTC (rev 1094)
@@ -230,6 +230,9 @@
return tmp;
}
+ /**
+ * Initialize console stream Files
+ */
protected static void initializeStdFiles()
{
try {
Modified: trunk/sight/java/org/jboss/sight/NativeObject.java
===================================================================
--- trunk/sight/java/org/jboss/sight/NativeObject.java 2007-10-10 06:28:46 UTC (rev 1093)
+++ trunk/sight/java/org/jboss/sight/NativeObject.java 2007-10-11 06:57:32 UTC (rev 1094)
@@ -50,6 +50,8 @@
throws OutOfMemoryError;
private static native void intr0(long instance);
+ private static native void sleep0(long instance, long step, long time);
+
/**
* Create new NativeObject without APR pool
*/
@@ -139,6 +141,11 @@
cbset0(INSTANCE, callback);
}
+ public void sleep(long step, long time)
+ {
+ sleep0(INSTANCE, step, time);
+ }
+
/**
* Destroy callback method.
* <BR/>
Modified: trunk/sight/native/include/sight_local.h
===================================================================
--- trunk/sight/native/include/sight_local.h 2007-10-10 06:28:46 UTC (rev 1093)
+++ trunk/sight/native/include/sight_local.h 2007-10-11 06:57:32 UTC (rev 1094)
@@ -361,8 +361,10 @@
/* XXX : BSD as more */
} sight_procstate_e;
-apr_status_t sight_pool_create(apr_pool_t **, apr_pool_t *, int);
+apr_status_t sight_pool_create(apr_pool_t **, apr_thread_mutex_t **,
+ apr_pool_t *, int);
apr_status_t sight_pool_clear(apr_pool_t *);
+apr_status_t sight_pool_destroy(apr_pool_t *);
apr_status_t sight_pool_lock(apr_thread_mutex_t **, apr_pool_t *);
apr_status_t sight_pool_lock_parent(apr_thread_mutex_t **, apr_pool_t *);
@@ -410,8 +412,10 @@
extern volatile apr_uint64_t sight_cnt_native_alloc;
extern volatile apr_uint64_t sight_cnt_native_create;
extern volatile apr_uint64_t sight_cnt_native_free;
+extern volatile apr_uint64_t sight_cnt_native_destroyed;
extern volatile apr_uint64_t sight_cnt_native_pcleanup;
extern volatile apr_uint64_t sight_cnt_native_clear;
+extern volatile apr_uint64_t sight_cnt_native_cleared;
extern volatile apr_uint64_t sight_cnt_native_clrcall;
extern void dbprintf(char *format, ...);
Modified: trunk/sight/native/include/sight_types.h
===================================================================
--- trunk/sight/native/include/sight_types.h 2007-10-10 06:28:46 UTC (rev 1093)
+++ trunk/sight/native/include/sight_types.h 2007-10-11 06:57:32 UTC (rev 1094)
@@ -117,13 +117,14 @@
volatile apr_uint32_t refcount;
#endif
volatile apr_uint32_t interrupted;
- apr_pool_t *pool;
- void *native;
- void *opaque;
- jobject object;
- jmethodID destroy;
- sight_callback_t cb;
- void (*clean)(int, sight_object_t *);
+ apr_pool_t *pool;
+ apr_thread_mutex_t *mutex;
+ void *native;
+ void *opaque;
+ jobject object;
+ jmethodID destroy;
+ sight_callback_t cb;
+ void (*clean)(int, sight_object_t *);
};
#define CACHE_HASH_MASK 255
Modified: trunk/sight/native/share/library.c
===================================================================
--- trunk/sight/native/share/library.c 2007-10-10 06:28:46 UTC (rev 1093)
+++ trunk/sight/native/share/library.c 2007-10-11 06:57:32 UTC (rev 1094)
@@ -32,6 +32,7 @@
#include "apr_atomic.h"
#include "sight_version.h"
+apr_pool_t *sight_master_pool = NULL;
apr_pool_t *sight_global_pool = NULL;
apr_pool_t *sight_temp_pool = NULL;
static JavaVM *sight_global_vm = NULL;
@@ -45,8 +46,10 @@
volatile apr_uint64_t sight_cnt_native_alloc = 0;
volatile apr_uint64_t sight_cnt_native_create = 0;
volatile apr_uint64_t sight_cnt_native_free = 0;
+volatile apr_uint64_t sight_cnt_native_destroyed = 0;
volatile apr_uint64_t sight_cnt_native_pcleanup = 0;
volatile apr_uint64_t sight_cnt_native_clear = 0;
+volatile apr_uint64_t sight_cnt_native_cleared = 0;
volatile apr_uint64_t sight_cnt_native_clrcall = 0;
static volatile apr_uint64_t sight_cnt_jvm_attached = 0;
@@ -168,18 +171,22 @@
sight_unload_classes(_E);
return JNI_FALSE;
}
- if (sight_pool_create(&sight_global_pool, NULL, 1) != APR_SUCCESS) {
+ if (apr_pool_create(&sight_master_pool, NULL) != APR_SUCCESS) {
return JNI_FALSE;
}
- if (sight_pool_create(&sight_temp_pool, NULL, 1) != APR_SUCCESS) {
+ if (apr_thread_mutex_create(&sight_global_mutex,
+ APR_THREAD_MUTEX_DEFAULT,
+ sight_master_pool) != APR_SUCCESS) {
return JNI_FALSE;
}
- if (apr_thread_mutex_create(&sight_global_mutex,
- APR_THREAD_MUTEX_DEFAULT,
- sight_global_pool) != APR_SUCCESS) {
+ if (sight_pool_create(&sight_global_pool, NULL, NULL, 1) != APR_SUCCESS) {
return JNI_FALSE;
}
+ if (sight_pool_create(&sight_temp_pool, NULL, NULL, 1) != APR_SUCCESS) {
+ return JNI_FALSE;
+ }
+
apr_atomic_init(sight_global_pool);
if (sight_main(sight_global_pool) != APR_SUCCESS) {
apr_pool_destroy(sight_temp_pool);
@@ -192,8 +199,8 @@
return JNI_TRUE;
}
-apr_status_t sight_pool_create(apr_pool_t **pool, apr_pool_t *parent,
- int lockable)
+apr_status_t sight_pool_create(apr_pool_t **pool, apr_thread_mutex_t **lock,
+ apr_pool_t *parent, int lockable)
{
apr_status_t rc;
apr_allocator_t *allocator = NULL;
@@ -210,12 +217,13 @@
if (lockable) {
if ((rc = apr_thread_mutex_create(&mutex,
APR_THREAD_MUTEX_DEFAULT,
- *pool)) != APR_SUCCESS) {
+ sight_master_pool)) != APR_SUCCESS) {
apr_allocator_destroy(allocator);
*pool = NULL;
return rc;
}
-
+ if (lock)
+ *lock = mutex;
}
else if (parent) {
/* Use parents allocator mutex */
@@ -228,23 +236,30 @@
apr_status_t sight_pool_clear(apr_pool_t *pool)
{
- apr_status_t rc;
apr_allocator_t *allocator = apr_pool_allocator_get(pool);
apr_thread_mutex_t *mutex = apr_allocator_mutex_get(allocator);
+ if (mutex)
+ apr_thread_mutex_lock(mutex);
apr_pool_clear(pool);
- if (mutex) {
- mutex = NULL;
- if ((rc = apr_thread_mutex_create(&mutex,
- APR_THREAD_MUTEX_DEFAULT,
- pool)) != APR_SUCCESS) {
- return rc;
- }
- }
- apr_allocator_mutex_set(allocator, mutex);
+ if (mutex)
+ apr_thread_mutex_unlock(mutex);
return APR_SUCCESS;
}
+apr_status_t sight_pool_destroy(apr_pool_t *pool)
+{
+ apr_allocator_t *allocator = apr_pool_allocator_get(pool);
+ apr_thread_mutex_t *mutex = apr_allocator_mutex_get(allocator);
+
+ if (mutex)
+ apr_thread_mutex_lock(mutex);
+ apr_pool_destroy(pool);
+ if (mutex)
+ apr_thread_mutex_unlock(mutex);
+ return APR_SUCCESS;
+}
+
apr_status_t sight_pool_lock(apr_thread_mutex_t **mutex, apr_pool_t *pool)
{
if (pool) {
@@ -268,8 +283,9 @@
apr_pool_t *parent = apr_pool_parent_get(pool);
apr_allocator_t *allocator = apr_pool_allocator_get(parent);
- if ((*mutex = apr_allocator_mutex_get(allocator)) != NULL)
+ if ((*mutex = apr_allocator_mutex_get(allocator)) != NULL) {
return apr_thread_mutex_lock(*mutex);
+ }
}
*mutex = NULL;
return APR_SUCCESS;
@@ -281,8 +297,10 @@
fprintf(stderr, "Allocated : %" APR_INT64_T_FMT
"\n", sight_cnt_native_alloc);
fprintf(stderr, "Initialized : %" APR_INT64_T_FMT
"\n", sight_cnt_native_create);
fprintf(stderr, "Garbage collected : %" APR_INT64_T_FMT
"\n", sight_cnt_native_free);
+ fprintf(stderr, "Garbage destroyed : %" APR_INT64_T_FMT
"\n", sight_cnt_native_destroyed);
fprintf(stderr, "Pool cleanup destroyed : %" APR_INT64_T_FMT
"\n", sight_cnt_native_pcleanup);
- fprintf(stderr, "Cleared : %" APR_INT64_T_FMT
"\n", sight_cnt_native_clear);
+ fprintf(stderr, "Clear called : %" APR_INT64_T_FMT
"\n", sight_cnt_native_clear);
+ fprintf(stderr, "Cleared : %" APR_INT64_T_FMT
"\n", sight_cnt_native_cleared);
fprintf(stderr, "Clean callbacks : %" APR_INT64_T_FMT
"\n", sight_cnt_native_clrcall);
fprintf(stderr, "\nJVM statistics ...............\n");
@@ -302,6 +320,8 @@
UNREFERENCED_STDARGS;
if (sight_global_pool) {
+ apr_thread_mutex_t *glock;
+ apr_thread_mutex_t *tlock;
#if SIGHT_APR_REFCOUNT
apr_uint32_t ioc = 0;
apr_uint32_t in_object_count = apr_atomic_dec32(sight_global_atomic);
@@ -310,8 +330,8 @@
fprintf(stderr, "\nLibrary terminate ............\n");
fprintf(stderr, "Global Atomic counter : %d\n", sight_global_a);
#endif
- sight_pool_lock(NULL, sight_temp_pool);
- sight_pool_lock(NULL, sight_global_pool);
+ sight_pool_lock(&tlock, sight_temp_pool);
+ sight_pool_lock(&glock, sight_global_pool);
#if SIGHT_APR_REFCOUNT
/* Wait until all JNI calls are done
@@ -337,9 +357,13 @@
}
#endif
apr_pool_destroy(sight_temp_pool);
+ sight_temp_pool = NULL;
apr_pool_destroy(sight_global_pool);
- sight_temp_pool = NULL;
sight_global_pool = NULL;
+ apr_thread_mutex_unlock(tlock);
+ apr_thread_mutex_unlock(glock);
+ apr_sleep(1000L);
+ apr_pool_destroy(sight_master_pool);
apr_terminate();
sight_unload_classes(_E);
}
@@ -348,6 +372,58 @@
#endif
}
+SIGHT_EXPORT_DECLARE(void, Library, clear0)(SIGHT_STDARGS)
+{
+
+ UNREFERENCED_O;
+
+ if (sight_global_pool) {
+#if SIGHT_APR_REFCOUNT
+ apr_uint32_t ioc = 0;
+ apr_uint32_t in_object_count = apr_atomic_dec32(sight_global_atomic);
+#endif
+ /* Wait untill all object native calls are finished */
+#ifdef SIGHT_DO_STATS
+ fprintf(stderr, "\nLibrary clear ................\n");
+ fprintf(stderr, "Global Atomic counter : %d\n", sight_global_a);
+#endif
+
+#if SIGHT_APR_REFCOUNT
+
+ while (in_object_count) {
+ apr_thread_yield();
+ in_object_count = apr_atomic_read32(sight_global_atomic);
+ }
+ if (ioc++ > OPTIMISTIC_LOCK_CNT) {
+ /* Sleep one millisecond */
+ apr_sleep(1000L);
+ }
+ if (ioc > OPTIMISTIC_LOCK_MAX) {
+ /* TODO: We have a zombie or a lengthy JNI op.
+ * Find a way to bail out without crushing JVM
+ */
+#ifdef SIGHT_DO_STATS
+ fprintf(stderr, "Library.clear0() call is locked ...\n");
+ fprintf(stderr, "Bailing out !\n");
+ fflush(stderr);
+#endif
+ throwAprException(_E, APR_TIMEUP);
+ return;
+ }
+
+#endif
+
+ sight_pool_clear(sight_temp_pool);
+ sight_pool_clear(sight_global_pool);
+ /* Sleep one second */
+ apr_sleep(1000L);
+ apr_atomic_set32(sight_global_atomic, 1);
+ }
+#ifdef SIGHT_DO_STATS
+ dump_stats();
+#endif
+}
+
SIGHT_EXPORT_DECLARE(jint, Library, version)(SIGHT_STDARGS, jint what)
{
apr_version_t apv;
@@ -400,64 +476,7 @@
return CSTR_TO_JSTRING(apr_version_string());
}
-SIGHT_EXPORT_DECLARE(void, Library, clear0)(SIGHT_STDARGS)
-{
- apr_status_t rc;
- UNREFERENCED_O;
- if (sight_global_pool) {
-#if SIGHT_APR_REFCOUNT
- apr_uint32_t ioc = 0;
- apr_uint32_t in_object_count = apr_atomic_dec32(sight_global_atomic);
-#endif
- /* Wait untill all object native calls are finished */
-#ifdef SIGHT_DO_STATS
- fprintf(stderr, "\nLibrary clear ................\n");
- fprintf(stderr, "Global Atomic counter : %d\n", sight_global_a);
-#endif
- sight_pool_lock(NULL, sight_temp_pool);
- sight_pool_lock(NULL, sight_global_pool);
-
-#if SIGHT_APR_REFCOUNT
-
- while (in_object_count) {
- apr_thread_yield();
- in_object_count = apr_atomic_read32(sight_global_atomic);
- }
- if (ioc++ > OPTIMISTIC_LOCK_CNT) {
- /* Sleep one millisecond */
- apr_sleep(1000L);
- }
- if (ioc > OPTIMISTIC_LOCK_MAX) {
- /* TODO: We have a zombie or a lengthy JNI op.
- * Find a way to bail out without crushing JVM
- */
-#ifdef SIGHT_DO_STATS
- fprintf(stderr, "Library.clear0() call is locked ...\n");
- fprintf(stderr, "Bailing out !\n");
- fflush(stderr);
-#endif
- throwAprException(_E, APR_TIMEUP);
- return;
- }
-
-#endif
-
- sight_pool_clear(sight_temp_pool);
- sight_pool_clear(sight_global_pool);
- apr_atomic_set32(sight_global_atomic, 1);
- if ((rc = apr_thread_mutex_create(&sight_global_mutex,
- APR_THREAD_MUTEX_DEFAULT,
- sight_global_pool)) != APR_SUCCESS) {
- throwAprException(_E, rc);
- return;
- }
- }
-#ifdef SIGHT_DO_STATS
- dump_stats();
-#endif
-}
-
/* Get current JNIEnv
* If this is a thread not created by JVM attach the thread
*/
Modified: trunk/sight/native/share/no.c
===================================================================
--- trunk/sight/native/share/no.c 2007-10-10 06:28:46 UTC (rev 1093)
+++ trunk/sight/native/share/no.c 2007-10-11 06:57:32 UTC (rev 1094)
@@ -87,6 +87,9 @@
JNIEnv *_E = NULL;
jint em = sight_get_jnienv(&_E);
+ apr_atomic_inc32(&no->interrupted);
+ /* Mark the pool as invaild */
+ no->pool = NULL;
if (_E && no->object) {
jobject object = (*_E)->NewLocalRef(_E, no->object);
(*_E)->DeleteWeakGlobalRef(_E, no->object);
@@ -101,13 +104,11 @@
no->object);
}
}
- SET_IFIELD_J(0000, no->object, 0);
SET_IFIELD_J(0001, no->object, 0);
(*_E)->DeleteLocalRef(_E, no->object);
no->object = NULL;
}
}
- apr_atomic_inc32(&no->interrupted);
#if SIGHT_APR_REFCOUNT
refcount = apr_atomic_read32(&no->refcount);
while (refcount) {
@@ -127,12 +128,13 @@
#endif
if (no->clean) {
(*no->clean)(POOL_CALLBACK, no);
+ no->clean = NULL;
}
if (_E && no->cb.object) {
(*_E)->DeleteGlobalRef(_E, no->cb.object);
+ no->cb.object = NULL;
}
sight_clr_jnienv(em);
- free(no);
}
return rv;
}
@@ -180,9 +182,17 @@
ppool = sight_global_pool;
}
if ((rc = sight_pool_lock(&mutex, ppool)) == APR_SUCCESS) {
- if ((rc = sight_pool_create(&pool, ppool, lock)) != APR_SUCCESS) {
+ if (!sight_global_pool) {
+ /* Global pool is destroyed */
throwAprMemoryException(_E, THROW_FMARK, rc);
apr_thread_mutex_unlock(mutex);
+ }
+ /* TODO: How to detect if parent pool was destroyed?
+ */
+ if ((rc = sight_pool_create(&pool, &no->mutex, ppool,
+ lock)) != APR_SUCCESS) {
+ throwAprMemoryException(_E, THROW_FMARK, rc);
+ apr_thread_mutex_unlock(mutex);
return;
}
@@ -247,7 +257,16 @@
return;
/* Check if parent is locked */
if (sight_pool_lock_parent(&mutex, no->pool) == APR_SUCCESS) {
+ apr_pool_t *pool = no->pool;
+#ifdef SIGHT_DO_STATS
+ sight_cnt_native_destroyed++;
+#endif
apr_atomic_inc32(&no->interrupted);
+ if (no->mutex) {
+ /* Lock only if we are the owner of the mutex */
+ apr_thread_mutex_lock(no->mutex);
+ }
+ no->pool = NULL;
if (no->object) {
object = (*_E)->NewLocalRef(_E, no->object);
(*_E)->DeleteWeakGlobalRef(_E, no->object);
@@ -275,8 +294,8 @@
}
}
#endif
- if (no->pool)
- apr_pool_cleanup_kill(no->pool, no, native_object_cleanup);
+ if (pool)
+ apr_pool_cleanup_kill(pool, no, native_object_cleanup);
if (no->object) {
(*_E)->CallVoidMethod(_E, no->object, no->destroy, NULL);
if ((*_E)->ExceptionCheck(_E)) {
@@ -286,12 +305,20 @@
}
if (no->clean)
(*no->clean)(POOL_DESTROY, no);
- if (no->pool)
- apr_pool_destroy(no->pool);
+ if (pool)
+ sight_pool_destroy(pool);
if (object)
(*_E)->DeleteLocalRef(_E, object);
- if (no->cb.object)
+ if (no->cb.object) {
(*_E)->DeleteGlobalRef(_E, no->cb.object);
+ no->cb.object = NULL;
+ }
+ if (no->mutex) {
+ apr_thread_mutex_unlock(no->mutex);
+ /* XXX: Check if we need some larger timeout here */
+ apr_thread_yield();
+ apr_thread_mutex_destroy(no->mutex);
+ }
if (mutex)
apr_thread_mutex_unlock(mutex);
free(no);
@@ -314,8 +341,22 @@
#endif
if (!no)
return;
+ /* Lock the parent pool.
+ * If the parent pool is inside clear or destroy
+ * this call will block until finished.
+ * By that time all pool cleanups will be run and
+ * the objects pool will be set to zero
+ */
if (sight_pool_lock_parent(&mutex, no->pool) == APR_SUCCESS) {
+ apr_pool_t *pool = no->pool;
+#ifdef SIGHT_DO_STATS
+ sight_cnt_native_cleared++;
+#endif
apr_atomic_inc32(&no->interrupted);
+ if (no->mutex) {
+ /* Lock only if we are the owner of the mutex */
+ apr_thread_mutex_lock(no->mutex);
+ }
#if SIGHT_APR_REFCOUNT
refcount = apr_atomic_read32(&no->refcount);
while (refcount) {
@@ -351,9 +392,11 @@
no->opaque = NULL;
no->native = NULL;
}
- apr_atomic_set32(&no->interrupted, 0);
+ if (no->mutex)
+ apr_thread_mutex_unlock(no->mutex);
if (mutex)
apr_thread_mutex_unlock(mutex);
+ apr_atomic_set32(&no->interrupted, 0);
}
}
@@ -367,3 +410,28 @@
apr_atomic_inc32(&no->interrupted);
}
}
+
+/* Temporary debug method for testing long native operations
+ */
+SIGHT_EXPORT_DECLARE(void, NativeObject, sleep0)(SIGHT_STDARGS,
+ jlong instance,
+ jlong step,
+ jlong time)
+{
+ sight_object_t *no = J2P(instance, sight_object_t *);
+
+ UNREFERENCED_STDARGS;
+ if (!no)
+ return;
+ SIGHT_LOCAL_TRY(no) {
+ jlong i;
+ for (i = 0; i < step; i++) {
+ if (SIGHT_LOCAL_IRQ(no)) {
+ SIGHT_LOCAL_BRK(no);
+ return;
+ }
+ apr_sleep(J2T(time));
+ }
+
+ } SIGHT_LOCAL_END(no);
+}
Modified: trunk/sight/test/org/jboss/sight/MemoryLeakTest.java
===================================================================
--- trunk/sight/test/org/jboss/sight/MemoryLeakTest.java 2007-10-10 06:28:46 UTC (rev
1093)
+++ trunk/sight/test/org/jboss/sight/MemoryLeakTest.java 2007-10-11 06:57:32 UTC (rev
1094)
@@ -45,7 +45,7 @@
super(0);
created++;
}
-
+
// Called from the native when the
// object is destroyed.
protected void onDestroy()
@@ -55,6 +55,46 @@
}
+ class NativeObjectRunner extends Thread
+ {
+ NativeObjectTestClass no;
+
+ NativeObjectRunner(NativeObjectTestClass no)
+ {
+ this.no = no;
+ }
+
+ public void run() {
+ try {
+ // Sleep 100 * 10 ms
+ no.sleep(100, 10000);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ class NativeObjectCleaner extends Thread
+ {
+ NativeObjectTestClass[] no;
+
+ NativeObjectCleaner(NativeObjectTestClass[] no)
+ {
+ this.no = no;
+ }
+
+ public void run() {
+ try {
+ for (int i = 0; i < no.length; i++) {
+ if (no[i] != null)
+ no[i].clear();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
public static void main(String[] args)
{
junit.textui.TestRunner.run(MemoryLeakTest.class);
@@ -72,11 +112,11 @@
Library.shutdown();
}
- public void testNativeObject()
+ public void testNativeObject0()
throws Exception
{
- for (int i = 0; i < 1000000; i++) {
+ for (int i = 0; i < 500000; i++) {
new NativeObjectTestClass();
}
// Library.clear() will force that all native objects
@@ -85,4 +125,64 @@
assertEquals(created, destroyed);
}
+ public void testNativeCalls1()
+ throws Exception
+ {
+
+ for (int i = 0; i < 1000; i++) {
+ new NativeObjectRunner(new NativeObjectTestClass()).start();
+ }
+ // Library.clear() will force that all native objects
+ // gets destroyed.
+ Library.clear();
+ assertEquals(created, destroyed);
+ }
+
+ public void testNativeCalls2()
+ throws Exception
+ {
+ int i;
+ NativeObjectTestClass[] no = new NativeObjectTestClass[1000];
+ for (i = 0; i < 1000; i++) {
+ no[i] = new NativeObjectTestClass();
+ }
+ for (i = 0; i < 1000; i++) {
+ new NativeObjectRunner(no[i]).start();
+ }
+ for (i = 0; i < 1000; i++) {
+ no[i].clear();
+ }
+
+ // Library.clear() will force that all native objects
+ // gets destroyed.
+ Library.clear();
+ assertEquals(created, destroyed);
+ }
+
+ public void testNativeCalls3()
+ throws Exception
+ {
+ int i;
+ NativeObjectTestClass[] no = new NativeObjectTestClass[1000];
+ for (i = 0; i < 1000; i++) {
+ no[i] = new NativeObjectTestClass();
+ }
+ for (i = 0; i < 1000; i++) {
+ new NativeObjectRunner(no[i]).start();
+ }
+ for (i = 0; i < 10; i++) {
+ new NativeObjectCleaner(no).start();
+ }
+ // Library.clear() will force that all native objects
+ // gets destroyed.
+ Library.clear();
+ assertEquals(created, destroyed);
+ for (i = 0; i < 1000; i++) {
+ no[i] = null;
+ }
+ System.gc();
+ Thread.sleep(1000);
+ System.gc();
+ }
+
}