Also, ich habe eine konzeptionelle Frage. Ich habe mit JNI für Android gearbeitet, um Low-Level-Audio-Dateien zu erstellen. Ich habe in C/C++ viel Audio-Kodierung gemacht, also dachte ich, dass das kein großes Problem wäre. Ich entschied mich, C++ in meinem "nativen" Code zu verwenden (denn wer liebt OOP nicht?). Das Problem, das mir begegnet ist, scheint (mir) seltsam zu sein: Wenn ich ein Objekt für die Verarbeitung von Audio im C++ - Code erstelle und dieses Objekt niemals an Java übergebe (oder andersherum), rufe Methoden darauf auf Objekt scheint die Garbage Collection ziemlich oft aufzurufen. Da dies innerhalb Audio-Rückrufe geschieht, ist das Ergebnis Stottern Audio, und ich erhalte häufig Nachrichten entlang der Linien von:Rufen C++ - Objekte in Android JNI-Code die Garbage Collection auf?
WAIT_FOR_CONCURRENT_GC blocked 23ms
Wenn jedoch fahre ich die gleichen Operationen durch statische Funktionen zu schaffen (und nicht als Aufruf an dem Mitglied Methoden ein Memeber-Objekt) scheint die Leistung der App in Ordnung zu sein, und ich sehe die obige Log-Nachricht nicht mehr.
Gibt es einen Grund, eine statische Funktion aufzurufen, sollte eine bessere Leistung haben als den Aufruf von Member-Methoden für ein Member-Objekt im systemeigenen Code? Genauer gesagt, sind Member-Objekte oder eingeschränkte Scope-Variablen, die vollständig im nativen Code eines JNI-Projekts enthalten sind, das an der Garbage Collection beteiligt ist? Ist der C++ - Aufrufstack in GC involviert? Gibt es irgendeinen Einblick, den irgendjemand mir geben kann, wie C++ Speichermanagement Java-Speichermanagement trifft, wenn es um JNI-Programmierung geht? Das heißt, in dem Fall, dass ich keine Daten zwischen Java und C++ übergebe, beeinflusst die Art, wie ich C++ Code schreibe, die Java-Speicherverwaltung (GC oder anders)?
Lassen Sie mich versuchen, ein Beispiel zu geben. Halte mit mir, denn es ist verdammt lang, und wenn du denkst, dass du Einsicht hast, kannst du hier mit dem Lesen aufhören.
Ich habe ein paar Objekte. Eines, das für das Erstellen der Audio-Engine, das Initialisieren der Ausgabe usw. verantwortlich ist. Es heißt HelloAudioJNI (tut mir leid, dass ich keine kompilierbaren Beispiele gemacht habe, aber es gibt eine Menge Code).
class CHelloAudioJNI {
... omitted members ...
//member object pointers
COscillator *osc;
CWaveShaper *waveShaper;
... etc ...
public:
//some methods
void init(float fs, int bufferSize, int channels);
... blah blah blah ...
Daraus folgt, dass ich ein paar mehr Klassen habe. Die WaveShaper Klasse sieht wie folgt aus:
class CWaveShaper : public CAudioFilter {
protected:
double *coeffs;
unsigned int order;//order
public:
CWaveShaper(const double sampleRate, const unsigned int numChannels,
double *coefficients, const unsigned int order);
double processSample(double input, unsigned int channel);
void reset();
};
Lassen Sie uns nicht für jetzt über die CAudioFilter Klasse kümmern, da dieses Beispiel schon ziemlich lang ist. Die WaveShaper CPP-Datei sieht wie folgt aus:
CWaveShaper::CWaveShaper(const double sampleRate,
const unsigned int numChannels,
double *coefficients,
const unsigned int numCoeffs) :
CAudioFilter(sampleRate,numChannels), coeffs(coefficients), order(numCoeffs)
{}
double CWaveShaper::processSample(double input, unsigned int channel)
{
double output = 0;
double pow = input;
//zeroth order polynomial:
output = pow * coeffs[0];
//each additional iteration
for(int iteration = 1; iteration < order; iteration++){
pow *= input;
output += pow * coeffs[iteration];
}
return output;
}
void CWaveShaper::reset() {}
und dann gibt es HelloAudioJNI.cpp. Hier kommen wir ins Thema. Ich eröffne die Mitglieder Objekte richtig, in der init-Funktion neu, so:
void CHelloAudioJNI::init(float samplerate, int bufferSize, int channels)
{
... some omitted initialization code ...
//wave shaper numero uno
double coefficients[2] = {1.0/2.0, 3.0/2.0};
waveShaper = new CWaveShaper(fs,outChannels,coefficients,2);
... some more omitted code ...
}
Ok scheint alles so weit in Ordnung. Dann innerhalb des Audio-Rückruf wir wie so einige Member-Methoden auf dem Element-Objekt aufrufen:
void CHelloAudioJNI::processOutputBuffer()
{
//compute audio using COscillator object
for(int index = 0; index < outputBuffer.bufferLen; index++){
for(int channel = 0; channel < outputBuffer.numChannels; channel++){
double sample;
//synthesize
sample = osc->computeSample(channel);
//wave-shape
sample = waveShaper->processSample(sample,channel);
//convert to FXP and save to output buffer
short int outputSample = amplitude * sample * FLOAT_TO_SHORT;
outputBuffer.buffer[interleaveIndex(index,channel)] = outputSample;
}
}
}
Dies ist, was häufige Audio-Unterbrechungen und viele Nachrichten über Garbage Collection produziert. Allerdings, wenn ich kopieren Sie die CWaveShaper :: Process() Funktion zum HelloAudioJNI.cpp unmittelbar über dem Rückruf und nenne es direkt anstelle die Elementfunktion:
sample = waveShape(sample, coeff, 2);
Dann bekomme ich schönen schönen Ton aus meinem Android kommt Gerät und ich bekomme keine so häufigen Nachrichten über Garbage Collection.Noch einmal sind die Fragen Mitgliederobjekte oder eingeschränkte Bereichsvariablen, die vollständig innerhalb des nativen Codes eines JNI-Projekts, das an der Garbage Collection beteiligt ist, leben? Ist der C++ - Aufrufstack in GC involviert? Gibt es irgendeinen Einblick, den irgendjemand mir geben kann, wie C++ Speichermanagement Java-Speichermanagement trifft, wenn es um JNI-Programmierung geht? Das heißt, in dem Fall, dass ich keine Daten zwischen Java und C++ übergebe, beeinflusst die Art, wie ich C++ Code schreibe, die Java-Speicherverwaltung (GC oder anders)?
Dies kann tangential sein, da Koeffizienten wie etwas klingen, das gelesen, aber nicht geschrieben würde. Daher sind die Werte beim Zugriff möglicherweise unbestimmt, aber es ist nicht sofort klar, dass eine Stapelkorruption auftreten würde. –
@ChrisStratton, können Sie Ihren Kommentar erklären? Ich verstehe nicht, was du meinst. – xaviersjs
Das hier angesprochene Problem sollte behoben werden, da die Werte der Koeffizienten, auf die von nicht mehr zugewiesenem Speicher zugegriffen wird, wahrscheinlich nicht korrekt sind. Aber wenn Sie später nicht versuchen, die Koeffizienten zu ändern, würde dies keinen fehlerhaften Programmablauf verursachen, was die einzige Erklärung für Ihre implizit durch diese Antwort angebotene Speicherbereinigung zu sein scheint. Wenn Sie nur die Koeffizienten lesen, die in einer Berechnung verwendet werden sollen, die für alle Eingaben gültig ist, dann ist "BadThings (tm)" auf "falsche Berechnungsergebnisse" beschränkt. –