I'm writing a Sqlite library for java which uses C Sqlite3 code over JNI.
I think I'm having a problem with memory leaks. Can anyone help me with reviewing my code and tell me what else i need to free?
here is my library:
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite3.h"
#include "dynamicArray.h"
void throwSqliteException(JNIEnv * env, const char * message) {
jclass ex = (*env)->FindClass(env, "sqliteNative/SqliteException");
(*env)->ThrowNew(env, ex, message);
}
JNIEXPORT jstring JNICALL Java_sqliteNative_Sqlite_getCVersion(JNIEnv * env, jclass obj) {
return (*env)->NewStringUTF(env, "1.0.3");
}
JNIEXPORT jobjectArray JNICALL Java_sqliteNative_Sqlite_query(JNIEnv * env, jobject obj, jstring database, jstring sql) {
const char * jDatabase = (*env)->GetStringUTFChars(env, database, NULL);
if (jDatabase == NULL)
return NULL;
const char * jSql = (*env)->GetStringUTFChars(env, sql, NULL);
if (jSql == NULL)
return NULL;
sqlite3 * db;
sqlite3_stmt * stmt;
const char * leftOver = 0;
jobjectArray table = NULL;
//printf("opening\n");
if (sqlite3_open(jDatabase, &db)) {
throwSqliteException(env, sqlite3_errmsg(db));
} else {
//printf("open ok\n");
const char * sqlExec = jSql;
// run queries for multiple sqls
// until left over is empty
do {
//printf("preparing\n");
int rc = sqlite3_prepare_v2(db, sqlExec, -1, &stmt, &leftOver);
//printf("prepare ok\n");
if (rc) {
throwSqliteException(env, sqlite3_errmsg(db));
break;
}
// change executing sql to next left over
sqlExec = leftOver;
//printf("getting coll count\n");
int cols = sqlite3_column_count(stmt);
//printf("coll count ok\n");
//printf("initing dynamic array\n");
dynamicArray * da = dynamicArray_init();
//printf("init ok\n");
jobjectArray row;
while (1) {
//printf("stepping\n");
int retval = sqlite3_step(stmt);
//printf("step ok\n");
if (retval == SQLITE_ROW) {
// only return data if it is a last executing sql
if (strlen(leftOver)) {
break;
}
//printf("creating row object array with size %d\n", cols);
row = (*env)->NewObjectArray(env, cols, (*env)->FindClass(env, "java/lang/String"), 0);
//printf("create ok\n");
jobjectArray * rowCopy = calloc(1, sizeof (row));
*rowCopy = row;
// add row to dynamic array
//printf("adding to da\n");
dynamicArray_add(da, rowCopy);
//printf("add ok\n");
int col;
for (col = 0; col < cols; col++) {
//printf("getting col value\n");
const char * val = (const char*) sqlite3_column_text(stmt, col);
//printf("value ok\n");
jstring valStr;
if (val == NULL) {
//printf("value is null\n");
//free((void*) val);
//printf("creating value string \"\"\n");
valStr = (*env)->NewStringUTF(env, "");
//printf("create ok\n");
} else {
//printf("creating value string from %s\n", val);
valStr = (*env)->NewStringUTF(env, val);
//printf("create ok\n");
}
//printf("adding value to row object array %d/%d\n", col + 1, cols);
(*env)->SetObjectArrayElement(env, row, col, valStr);
//printf("add ok\n");
}
} else if (retval == SQLITE_DONE) {
break;
} else {
throwSqliteException(env, sqlite3_errmsg(db));
break;
}
}
// copy data from dynamic array to java's String[][]
if (da->elements > 0) {
//printf("creating object array table\n");
table = (*env)->NewObjectArray(env, da->elements, (*env)->GetObjectClass(env, row), 0);
//printf("create ok\n");
int i = 0;
for (; i < da->elements; i++) {
//printf("getting object from da\n");
jobjectArray item = *(jobjectArray *) da->array[i];
//printf("get ok\n");
//printf("adding object row to table\n");
(*env)->SetObjectArrayElement(env, table, i, item);
//free(da->array[i]);
//printf("add ok\n");
}
}
// free memory
//printf("freeing da memory\n");
dynamicArray_free(da);
//printf("free ok\n");
} while (strlen(leftOver));
}
// free memory
//printf("closing database\n");
//sqlite3_free(stmt);
sqlite3_close(db);
//printf("close ok\n");
//printf("releasing jdatabase\n");
(*env)->ReleaseStringUTFChars(env, database, jDatabase);
//printf("release ok\n");
//printf("releasing jsql\n");
(*env)->ReleaseStringUTFChars(env, sql, jSql);
//printf("release ok\n");
return table;
}
basically function opens database, executes query, makes a java 2d array out of it and send it back. Database is closed at the end.
Here is my dynamic array implementation:
#include "dynamicArray.h"
#include <stdlib.h>
void dynamicArray_add(dynamicArray * da, void * item) {
if (da->elements == da->allocated) {
if (da->allocated == 0)
da->allocated = 3;
else
da->allocated *= 2;
void *_tmp = realloc(da->array, (da->allocated * sizeof (item)));
if (!_tmp) {
return;
}
da->array = (void**) _tmp;
}
da->array[da->elements] = item;
da->elements++;
}
dynamicArray * dynamicArray_init() {
dynamicArray * da = calloc(1, sizeof (dynamicArray));
return da;
}
void dynamicArray_free(dynamicArray * da) {
free(da->array);
free(da);
}
and java test code
public void testQueryLoop() throws Exception {
Sqlite.query("testna.db", "drop table if exists user");
Sqlite.query("testna.db", "create table user (username, password)");
Sqlite.query("testna.db", "insert into user values ('u1', 'a')");
Sqlite.query("testna.db", "insert into user values('u2', 'b1');");
for (int i = 0; i < 1010; i++) {
if (i % 500 == 0) {
System.out.println("gcing");
Runtime.getRuntime().gc();
}
System.out.println(i);
String[][] r = Sqlite.query("testna.db", "select * from user");
assertEquals(2, r.length);
assertEquals("u1", r[0][0]);
assertEquals("b1", r[1][1]);
}
}
(that garbage collection doesn't help)
Loop crashes after 1003 iterations.
This is the error i get
# # A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc=0x01179d1a, pid=5114, tid=3079269232 # # JRE version: 6.0_20-b20 # Java VM: OpenJDK Client VM (19.0-b09 mixed mode, sharing linux-x86 ) # Derivative: IcedTea6 1.9.4 # Distribution: Ubuntu 10.10, package 6b20-1.9.4-0ubuntu1 # Problematic frame: # V [libjvm.so+0x21cd1a] # # Can not save log file, dump to screen.. # # A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc=0x01179d1a, pid=5114, tid=3079269232 # # JRE version: 6.0_20-b20 # Java VM: OpenJDK Client VM (19.0-b09 mixed mode, sharing linux-x86 ) # Derivative: IcedTea6 1.9.4 # Distribution: Ubuntu 10.10, package 6b20-1.9.4-0ubuntu1 # Problematic frame: # V [libjvm.so+0x21cd1a] # # If you would like to submit a bug report, please include # instructions how to reproduce the bug and visit: # https://bugs.launchpad.net/ubuntu/+source/openjdk-6/ # --------------- T H R E A D --------------- Current thread (0x09a9dc00): JavaThread "main" [_thread_in_vm, id=5116, stack(0xb784e000,0xb789f000)] siginfo:si_signo=SIGSEGV: si_errno=0, si_code=1 (SEGV_MAPERR), si_addr=0x00000000 Registers: EAX=0x00000000, EBX=0x013baff4, ECX=0x09a9e3d4, EDX=0x09a9dc00 ESP=0xb789d644, EBP=0xb789d6a8, ESI=0x09a9dc00, EDI=0x09a9dc00 EIP=0x01179d1a, CR2=0x00000000, EFLAGS=0x00210286 Register to memory mapping: EAX=0x00000000 0x00000000 is pointing to unknown location EBX=0x013baff4 0x013baff4: in /usr/lib/jvm/java-6-openjdk/jre/lib/i386/client/libjvm.so at 0x00f5d000 ECX=0x09a9e3d4 0x09a9e3d4 is pointing to unknown location EDX=0x09a9dc00 "main" prio=10 tid=0x09a9dc00 nid=0x13fc runnable [0xb789d000] java.lang.Thread.State: RUNNABLE ESP=0xb789d644 0xb789d644 is pointing into the stack for thread: 0x09a9dc00 "main" prio=10 tid=0x09a9dc00 nid=0x13fc runnable [0xb789d000] java.lang.Thread.State: RUNNABLE EBP=0xb789d6a8 0xb789d6a8 is pointing into the stack for thread: 0x09a9dc00 "main" prio=10 tid=0x09a9dc00 nid=0x13fc runnable [0xb789d000] java.lang.Thread.State: RUNNABLE ESI=0x09a9dc00 "main" prio=10 tid=0x09a9dc00 nid=0x13fc runnable [0xb789d000] java.lang.Thread.State: RUNNABLE EDI=0x09a9dc00 "main" prio=10 tid=0x09a9dc00 nid=0x13fc runnable [0xb789d000] java.lang.Thread.State: RUNNABLE Top of Stack: (sp=0xb789d644) 0xb789d644: 09a9dc00 013d15e0 013d15e0 00000000 0xb789d654: 00000000 b789d66c 09a9e3d8 8fa9fa98 0xb789d664: b789d67c 00ea5238 09a9dc00 00000000 0xb789d674: ffffffff 00d29ff4 09a9dc00 09a9e3d0 0xb789d684: 00000088 013926a8 09a9dc00 00000000 0xb789d694: 00000001 00000005 00f28ff4 8fa9fa98 0xb789d6a4: 09a9dc00 b789d6d8 00ea8e49 09a9dd18 0xb789d6b4: 00000000 00f25b42 09a9dc00 b789d758 Instructions: (pc=0x01179d1a) 0x01179d0a: 52 89 75 e4 52 56 51 e8 6a be 00 00 58 8b 45 0c 0x01179d1a: 8b 38 57 e8 fe 7a fe ff 89 c7 83 c4 10 83 c0 08 Stack: [0xb784e000,0xb789f000], sp=0xb789d644, free space=317k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) V [libjvm.so+0x21cd1a] C [libSqlite.so+0x4e49] throwSqliteException+0x4d C [libSqlite.so+0x4f3f] Java_sqliteNative_Sqlite_query+0xbb j sqliteNative.Sqlite.query(Ljava/lang/String;Ljava/lang/String;)[[Ljava/lang/String;+0 j sqliteNative.SqliteTest.testQueryLoop()V+74 v ~StubRoutines::call_stub V [libjvm.so+0x203db2] V [libjvm.so+0x302879] V [libjvm.so+0x202d4f] V [libjvm.so+0x33f164] V [libjvm.so+0x33fdfe] V [libjvm.so+0x258d01] C [libjava.so+0x149c2] Java_sun_reflect_NativeMethodAccessorImpl_invoke0+0x32 j sun.reflect.NativeMethodAccessorImpl.invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+0 j sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87 j sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6 j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+161 j org.junit.runners.model.FrameworkMethod$1.runReflectiveCall()Ljava/lang/Object;+15 j org.junit.internal.runners.model.ReflectiveCallable.run()Ljava/lang/Object;+1 j org.junit.runners.model.FrameworkMethod.invokeExplosively(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+10 j org.junit.internal.runners.statements.InvokeMethod.evaluate()V+12 j org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(Lorg/junit/runners/model/FrameworkMethod;Lorg/junit/internal/runners/model/EachTestNotifier;)V+9 j org.junit.runners.BlockJUnit4ClassRunner.runChild(Lorg/junit/runners/model/FrameworkMethod;Lorg/junit/runner/notification/RunNotifier;)V+28 j org.junit.runners.BlockJUnit4ClassRunner.runChild(Ljava/lang/Object;Lorg/junit/runner/notification/RunNotifier;)V+6 j org.junit.runners.ParentRunner$3.run()V+12 j org.junit.runners.ParentRunner$1.schedule(Ljava/lang/Runnable;)V+1 j org.junit.runners.ParentRunner.runChildren(Lorg/junit/runner/notification/RunNotifier;)V+40 j org.junit.runners.ParentRunner.access$000(Lorg/junit/runners/ParentRunner;Lorg/junit/runner/notification/RunNotifier;)V+2 j org.junit.runners.ParentRunner$2.evaluate()V+8 j org.junit.internal.runners.statements.RunBefores.evaluate()V+49 j org.junit.internal.runners.statements.RunAfters.evaluate()V+18 j org.junit.runners.ParentRunner.run(Lorg/junit/runner/notification/RunNotifier;)V+20 j junit.framework.JUnit4TestAdapter.run(Ljunit/framework/TestResult;)V+13 j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run()V+431 j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(Lorg/apache/tools/ant/taskdefs/optional/junit/JUnitTest;ZZZZZZ)I+39 j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main([Ljava/lang/String;)V+781 v ~StubRoutines::call_stub V [libjvm.so+0x203db2] V [libjvm.so+0x302879] V [libjvm.so+0x202d4f] V [libjvm.so+0x20d9f4] V [libjvm.so+0x2277cd] C [java+0x2ef5] JavaMain+0xd45 C [libpthread.so.0+0x5cc9] Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) j sqliteNative.Sqlite.query(Ljava/lang/String;Ljava/lang/String;)[[Ljava/lang/String;+0 j sqliteNative.SqliteTest.testQueryLoop()V+74 v ~StubRoutines::call_stub j sun.reflect.NativeMethodAccessorImpl.invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+0 j sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+87 j sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+6 j java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+161 j org.junit.runners.model.FrameworkMethod$1.runReflectiveCall()Ljava/lang/Object;+15 j org.junit.internal.runners.model.ReflectiveCallable.run()Ljava/lang/Object;+1 j org.junit.runners.model.FrameworkMethod.invokeExplosively(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+10 j org.junit.internal.runners.statements.InvokeMethod.evaluate()V+12 j org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(Lorg/junit/runners/model/FrameworkMethod;Lorg/junit/internal/runners/model/EachTestNotifier;)V+9 j org.junit.runners.BlockJUnit4ClassRunner.runChild(Lorg/junit/runners/model/FrameworkMethod;Lorg/junit/runner/notification/RunNotifier;)V+28 j org.junit.runners.BlockJUnit4ClassRunner.runChild(Ljava/lang/Object;Lorg/junit/runner/notification/RunNotifier;)V+6 j org.junit.runners.ParentRunner$3.run()V+12 j org.junit.runners.ParentRunner$1.schedule(Ljava/lang/Runnable;)V+1 j org.junit.runners.ParentRunner.runChildren(Lorg/junit/runner/notification/RunNotifier;)V+40 j org.junit.runners.ParentRunner.access$000(Lorg/junit/runners/ParentRunner;Lorg/junit/runner/notification/RunNotifier;)V+2 j org.junit.runners.ParentRunner$2.evaluate()V+8 j org.junit.internal.runners.statements.RunBefores.evaluate()V+49 j org.junit.internal.runners.statements.RunAfters.evaluate()V+18 j org.junit.runners.ParentRunner.run(Lorg/junit/runner/notification/RunNotifier;)V+20 j junit.framework.JUnit4TestAdapter.run(Ljunit/framework/TestResult;)V+13 j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run()V+431 j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(Lorg/apache/tools/ant/taskdefs/optional/junit/JUnitTest;ZZZZZZ)I+39 j org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main([Ljava/lang/String;)V+781 v ~StubRoutines::call_stub --------------- P R O C E S S --------------- Java Threads: ( => current thread ) 0x09ad9000 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=5123, stack(0xb4fc3000,0xb5014000)] 0x09ad7400 JavaThread "CompilerThread0" daemon [_thread_blocked, id=5122, stack(0xb5014000,0xb5095000)] 0x09ad5800 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=5121, stack(0xb5095000,0xb50e6000)] 0x09acdc00 JavaThread "Finalizer" daemon [_thread_blocked, id=5120, stack(0xb52e6000,0xb5337000)] 0x09acc400 JavaThread "Reference Handler" daemon [_thread_blocked, id=5119, stack(0xb5337000,0xb5388000)] =>0x09a9dc00 JavaThread "main" [_thread_in_vm, id=5116, stack(0xb784e000,0xb789f000)] Other Threads: 0x09acac00 VMThread [stack: 0xb5388000,0xb5409000] [id=5118] 0x09ae5000 WatcherThread [stack: 0xb4f42000,0xb4fc3000] [id=5124] VM state:not at safepoint (normal execution) VM Mutex/Monitor currently owned by a thread: None Heap def new generation total 4928K, used 83K [0x70310000, 0x70860000, 0x7aa60000) eden space 4416K, 1% used [0x70310000, 0x70324dd0, 0x70760000) from space 512K, 0% used [0x70760000, 0x70760000, 0x707e0000) to space 512K, 0% used [0x707e0000, 0x707e0000, 0x70860000) tenured generation total 10752K, used 579K [0x7aa60000, 0x7b4e0000, 0x8f910000) the space 10752K, 5% used [0x7aa60000, 0x7aaf0cf0, 0x7aaf0e00, 0x7b4e0000) compacting perm gen total 12288K, used 1605K [0x8f910000, 0x90510000, 0x93910000) the space 12288K, 13% used [0x8f910000, 0x8faa1760, 0x8faa1800, 0x90510000) ro space 10240K, 73% used [0x93910000, 0x94066d50, 0x94066e00, 0x94310000) rw space 12288K, 60% used [0x94310000, 0x94a4f9f8, 0x94a4fa00, 0x94f10000) Dynamic libraries: Can not get library information for pid = 5116 VM Arguments: jvm_args: -Djava.library.path=/home/vojko/NetBeansProjects/Sqlite/dist/Debug/GNU-Linux-x86/ java_command: org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner sqliteNative.SqliteTest filtertrace=true haltOnError=false haltOnFailure=false showoutput=true outputtoformatters=true logfailedtests=true logtestlistenerevents=true formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,/home/vojko/NetBeansProjects/sqliteNative/build/test/results/TEST-sqliteNative.SqliteTest.xml crashfile=/home/vojko/NetBeansProjects/sqliteNative/build/junitvmwatcher6420726485344985293.properties propsfile=/home/vojko/NetBeansProjects/sqliteNative/build/junit7069577112414567858.properties Launcher Type: SUN_STANDARD Environment Variables: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games USERNAME=vojko LD_LIBRARY_PATH=/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/jvm/java-6-openjdk/jre/../lib/i386 SHELL=/bin/bash DISPLAY=:0.0 Signal Handlers: SIGSEGV: [libjvm.so+0x3df4f0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004 SIGBUS: [libjvm.so+0x3df4f0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004 SIGFPE: [libjvm.so+0x3055c0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004 SIGPIPE: [libjvm.so+0x3055c0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004 SIGXFSZ: [libjvm.so+0x3055c0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004 SIGILL: [libjvm.so+0x3055c0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004 SIGUSR1: SIG_DFL, sa_mask[0]=0x00000000, sa_flags=0x00000000 SIGUSR2: [libjvm.so+0x304c70], sa_mask[0]=0x00000004, sa_flags=0x10000004 SIGHUP: [libjvm.so+0x3077e0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004 SIGINT: SIG_IGN, sa_mask[0]=0x00000000, sa_flags=0x00000000 SIGTERM: [libjvm.so+0x3077e0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004 SIGQUIT: [libjvm.so+0x3077e0], sa_mask[0]=0x7ffbfeff, sa_flags=0x10000004 --------------- S Y S T E M --------------- OS:Linux uname:Linux 2.6.35-25-generic #44-Ubuntu SMP Fri Jan 21 17:40:48 UTC 2011 i686 libc:glibc 2.12.1 NPTL 2.12.1 rlimit: STACK 8192k, CORE 0k, NPROC infinity, NOFILE 1024, AS infinity load average:0.00 0.00 0.00 /proc/meminfo: CPU:total 1 (1 cores per cpu, 1 threads per core) family 6 model 37 stepping 2, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3, sse4.1, sse4.2, popcnt Memory: 4k page, physical 1025708k(42264k free), swap 916476k(902956k free) vm_info: OpenJDK Client VM (19.0-b09) for linux-x86 JRE (1.6.0_20-b20), built on Jan 7 2011 07:24:55 by "buildd" with gcc 4.4.5 time: Thu Feb 3 04:41:48 2011 elapsed time: 1 seconds # # If you would like to submit a bug report, please include # instructions how to reproduce the bug and visit: # https://bugs.launchpad.net/ubuntu/+source/openjdk-6/ # Testsuite: sqliteNative.SqliteTest Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 0 sec Testcase: sqliteNative.SqliteTest:testQueryLoop: Caused an ERROR Forked Java VM exited abnormally. Please note the time in the report does not reflect the time until the VM exit. junit.framework.AssertionFailedError: Forked Java VM exited abnormally. Please note the time in the report does not reflect the time until the VM exit. at org.netbeans.core.execution.RunClassThread.run(Unknown Source)
Does anyone have any suggestions?
Thanks
sqlite_free(stmt);
- that's a very important call – NG.