PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [Java/C]JNI + Cygwin: malloc macht Probleme


Gast
2012-04-04, 12:12:01
Hallo,

ich habe folgendes Problem mit Cygwin. Ich habe eine Java-Klasse mit einer nativen Methode:
package de.jni.test;

public class JniClass {
public static native double processArray(double[] array);
}
die per JNI in C ausgeführt werden soll. Auf der C-Seite ist die Methode/Funktion folgendermaßen implementiert:
JNIEXPORT jdouble JNICALL Java_de_jni_test_JniClass_processArray
(JNIEnv * env, jobject obj, jdoubleArray array_J)
{
// get size of Java array
int size = (*env)->GetArrayLength(env, array_J);
// create C array of same size
double *array_C = (double*) malloc(size * sizeof(double));
// copy elements from Java array to C array
(*env)->GetDoubleArrayRegion(env, array_J, 0, size, array_C);
// do some funny stuff to C array
double sum = 0;
int i;
for (i=0; i < size; i++)
{
sum += array_C[i];
array_C[i] += sum;
}
// copy back elements from C array to Java array
(*env)->SetDoubleArrayRegion(env, array_J, 0, size, array_C);
// release C array
free(array_C);
return sum;
}
Es wird also der Inhalt des übergebenen Java-Arrays in ein C-Array kopiert, das mit malloc erzeugt wird. Mit den Elementen des C-Arrays wird dann etwas gemacht, und anschließend werden sie in das Java-Array zurückkopiert. Das C-Array wird dann freigegeben.

Das ganz funktioniert auch wunderbar, wenn ich die C-DLL mit MinGW kompiliere. Baue ich sie stattdessen mit Cygwin, so stürzt bei Aufruf der Methode auf Java-Seite die JVM ab, mit folgender Fehlermeldung:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x610d3494, pid=4576, tid=6784
#
# JRE version: 6.0_20-b02
# Java VM: Java HotSpot(TM) Client VM (16.3-b01 mixed mode windows-x86 )
# Problematic frame:
# C [cygwin1.dll+0xd3494]
#
# An error report file with more information is saved as:
# C:\Users\scholten\workspace\JniTest_Java\hs_err_pid4576.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

Offenbar scheint das Problem von malloc herzurühren, denn wenn ich die Funktion in C stattdessen folgendermaßen implementiere:
JNIEXPORT jdouble JNICALL Java_de_jni_test_JniClass_processArray
(JNIEnv * env, jobject obj, jdoubleArray array_J)
{
// size of Java array
int size = (*env)->GetArrayLength(env, array_J);
// create C array and copy elements from Java array
(*env)->GetDoubleArrayElements(env, array_J, NULL);
// do some funny stuff to C array
double sum = 0;
int i;
for (i=0; i < size; i++)
{
sum += array_C[i];
array_C[i] += sum;
}
// copy back elements from C array to Java array and release C array
(*env)->ReleaseDoubleArrayElements(env, array_J, array_C, 0);
return sum;
}
d.h. ohne malloc() und free(), und dafür stattdessen mit den von JNI gelieferten Funktionen zum Erzeugen und Freigeben von C-Arrays, dann klappt alles auch unter Cygwin.

Leider kann ich auf malloc() nicht verzichten und auch nicht MinGW statt Cygwin benutzen, da ich eine C-Bibliothek per JNI ansprechen muss, die nur unter Cygwin kompilierfähig ist, und die massiven Gebrauch von malloc macht.

Jemand eine Idee?

Danke im voraus.

Gast
2012-04-04, 15:42:05
Ich habe jetzt einen Workaround gefunden. Und zwar funktioniert unter Cygwin die C++ Version, in der ich die Zeilen
double *array_C = (double*) malloc(size * sizeof(double));
// ...
free(array_C);
ersetze durch
double *array_C = new double[size];
// ...
delete [] array_C;
Ich kann zwar leider nicht alle mallocs durch news ersetzen, aber ich kann die Standardimplementierung von malloc und free ersetzen:
_PTR malloc(size_t __size)
{
void* ptr = new char[__size];
return ptr;
}

_VOID free(_PTR ptr)
{
delete [] ptr;
}
Das ist zwar nicht gerade schön, funktioniert aber tatsächlich so weit.

Ich schätze, dann wird Cygwin so aufgebaut sein, dass es getrennte Standardbibliotheken für C und C++ gibt, und malloc die für C benutzt und new für die C++, und die new-Implementierung in der C++ Bibliothek keinen Gebrauch von der C-Bibliothek macht. Und dass die C-Bibliotheken mit JNI nicht klarkommen, die C++ Bibliotheken aber schon. Jetzt wäre es natürlich praktisch, wenn es in den C++ Bibliotheken ein malloc gäbe und man dieses direkt nutzen könnte...

Gast
2012-05-03, 10:49:51
Keiner eine Idee? Hat denn von euch jemand Erfahrung mit JNI in Verbindung mit Cygwin?

Ectoplasma
2012-05-07, 10:32:50
Bist du dir wirklich sicher, dass du die richtige Länge mit malloc allozierst? Leider kenne ich die JNI Doku nicht, darum weiss ich auch nicht, mit welcher Größe Arrays alloziert werden sollten. Ich kann mir beim besten Willen nicht vorstellen, dass ausgerechnet cywin's malloc nicht richtig funktioniert. Wenn du new[] verwendest, dann wird auch immer ein wenig mehr alloziert, als du eigentlich benötigst (für die interne Verwaltung). Das könnte hier also eher Zufall sein, dass es läuft. Letztendlich benutzt "new" intern auch wieder nur "malloc".

Gast
2012-05-07, 13:55:14
Bist du dir wirklich sicher, dass du die richtige Länge mit malloc allozierst? Leider kenne ich die JNI Doku nicht, darum weiss ich auch nicht, mit welcher Größe Arrays alloziert werden sollten. eigentlich sollte malloc() Arrays beliebiger Länge allokieren können.

Ich kann mir beim besten Willen nicht vorstellen, dass ausgerechnet cywin's malloc nicht richtig funktioniert. Wenn du new[] verwendest, dann wird auch immer ein wenig mehr alloziert, als du eigentlich benötigst (für die interne Verwaltung). Das könnte hier also eher Zufall sein, dass es läuft. Letztendlich benutzt "new" intern auch wieder nur "malloc".Wie ich schon schrieb, scheint es so zu sein, dass Cygwin getrennte Bibliotheken für C und C++ hat. Das new ist in der C++ Bibliothek implementiert, und bildet dort eventuell auf ein malloc() ab, das ebenfalls dort implementiert ist. Ruft man aber explizit malloc() statt new auf, so wird das malloc() auf der C-Bibliothek genommen, und dieses macht offenbar Ärger. Wenn man das malloc() aus der C++ Bibliothek nutzen könnte, wäre das praktisch.

del_4901
2012-05-07, 14:22:53
probier mal ein assert(_CrtCheckMemory()) oder Äquivalent nachdem der Buffer kopiert wurde.

Ectoplasma
2012-05-07, 15:03:42
Ok. Hast du es einmal mit dem Aufruf "v = (double *)operator new(size);" probiert? Der Aufruf ist genauso untypisiert, wie malloc. Du kannst den Heap einfach mit "delete v;" wieder freigeben. Wenn das funktionieren sollte, dann ist wohl tatsächlich irgendetwas faul mit malloc. Aber das kanns doch irgendwie nicht sein. Das muss doch dann eine faule cygwin lib sein. Es widerspricht jeder Logik, wenn alles andere sonst mit malloc läuft.

Epeios
2012-05-07, 17:47:27
Hallo zusammen !

Ich habe das selbe Problem. Ein Java Komponente das lauft einwandfrei unter Mac OS, Linux und Windows (wenn mit Visual C++ kompiliert). Aber wenn mit Cygwin kompiliert, dann hab ich die 'EXCEPTION_ACCESS_VIOLATION ...' Meldung oder das Programm ist blockiert, das selbst ein 'CTRL-C' ihn nicht stoppt. Mit Hilfe von 'gdb' habe ich gemerkt das es an 'malloc()' liegt. Zusätzlich, den C++ Code das ich für den Java Komponente nütze nutze ich auch in eine einfache Kommandozeile Programmdatei (einen '.exe') wie mit Cygwin kompiliert ist und auch einwandfrei lauft.

Wen ich 'man malloc' unter Cygwin mache, da steht, das für Programme die 'multithread' sind, dann sollte man möglichst nicht 'malloc()' (oder 'realloc()', 'free()'...) nutzen aber '_malloc_r()' (oder '_realloc_r()', '_free_r()'...). Mit 'gdb' sieht man das Java mehrere 'threads' kreiert, so ist es wohl möglich dass das Problem davon kommt. Leider scheint die Nutzung von '_malloc_r()' und verwandte ziemlich kompliziert ...

Sie können mehr erfahren über den Java Komponente und Kommandozeile Programmdatei von dessen ich schreibe an diese Adresse : http://zeusw.org/intl/expp/

(Entschuldigung für mein lausigen Deutsch ; ich bin Franzose...)

Claude SIMON (alias Epeios)
http://zeusw.org/