Note that there are some explanatory texts on larger screens.

plurals
  1. PODo C++ objects in Android JNI native code invoke garbage collection?
    primarykey
    data
    text
    <p>So, I've got a conceptual question. I've been working with JNI on Android for the purposes of doing low-level audio "stuff." I've done plenty of audio coding in C/C++, so I figured that this would not be much of a problem. I decided to do use C++ in my "native" code (because who doesn't love OOP?). The issue I've encountered seems (to me) to be a strange one: when I create an object for processing audio in the C++ code, and I never pass this object to Java (nor the other way around), calling methods on this object seems to invoke the garbage collection quite often. Since this is happening inside audio callbacks, the result is stuttering audio, and I get frequent messages along the lines of:</p> <pre><code>WAIT_FOR_CONCURRENT_GC blocked 23ms </code></pre> <p>However, when I perform the same operations by creating static functions (rather than invoking member methods on a memeber object) the performance of the app seems to be fine, and I no longer see the above log message.</p> <p><strong>Basically, is there any reason calling a static function should have better performance than calling member methods on a member object in native code?</strong> More specifically, are member objects, or limited scope variables that live entirely inside the native code of a JNI project involved in garbage collection? Is the C++ call stack involved in GC? Is there any insight anybody can give me on how C++ memory management meets Java memory management when it comes to JNI programming? That is, in the case that I'm not passing data between Java and C++, does the way I write C++ code affect Java memory management (GC or otherwise)?</p> <p>Allow me to try to give an example. Bear with me, 'cause it's freaking long, and if you think you have insight you're welcome to stop reading here. </p> <p>I have a couple of objects. One that is responsible for creating the audio engine, initializing output, etc. It is called HelloAudioJNI (sorry for not putting compile-able examples, but there's a lot of code).</p> <pre><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 ... </code></pre> <p>It follows that I have a couple more classes. The WaveShaper class looks like this:</p> <pre><code>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(); }; </code></pre> <p>Let's not worry about the CAudioFilter class for now, since this example is already quite long. The WaveShaper .cpp file looks like this:</p> <pre><code>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 &lt; order; iteration++){ pow *= input; output += pow * coeffs[iteration]; } return output; } void CWaveShaper::reset() {} </code></pre> <p>and then there's HelloAudioJNI.cpp. This is where we get into the meat of the issue. I create the member objects properly, using new inside the init function, thusly:</p> <pre><code>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 ... } </code></pre> <p>Ok everything seems fine so far. Then inside the audio callback we call some member methods on the member object like so:</p> <pre><code>void CHelloAudioJNI::processOutputBuffer() { //compute audio using COscillator object for(int index = 0; index &lt; outputBuffer.bufferLen; index++){ for(int channel = 0; channel &lt; outputBuffer.numChannels; channel++){ double sample; //synthesize sample = osc-&gt;computeSample(channel); //wave-shape sample = waveShaper-&gt;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; } } } </code></pre> <p>This is what produces frequent audio interruptions and lots of messages about garbage collection. However, if I copy the CWaveShaper::processSample() function to the HelloAudioJNI.cpp immediately above the callback and call it directly instead of the member function:</p> <pre><code>sample = waveShape(sample, coeff, 2); </code></pre> <p>Then I get beautiful beautiful audio coming out of my android device and I do not get such frequent messages about garbage collection. Once again the questions are, <strong>are member objects, or limited scope variables that live entirely inside the native code of a JNI project involved in garbage collection? Is the C++ call stack involved in GC? Is there any insight anybody can give me on how C++ memory management meets Java memory management when it comes to JNI programming? That is, in the case that I'm not passing data between Java and C++, does the way I write C++ code affect Java memory management (GC or otherwise)?</strong></p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload