所以,我有一个概念性的问题。我一直在使用 Android 上的 JNI 来做低级音频“东西”。我已经用 C/C++ 完成了大量的音频编码,所以我认为这不是什么大问题。我决定在我的“本机”代码中使用 C++(因为谁不喜欢 OOP?)。我遇到的问题似乎(对我来说)是一个奇怪的问题:当我在 C++ 代码中创建一个用于处理音频的对象时,我从未将此对象传递给 Java(也没有相反的方式),在此调用方法对象似乎经常调用垃圾收集。由于这发生在音频回调中,结果是音频断断续续,我经常收到以下消息:
WAIT_FOR_CONCURRENT_GC blocked 23ms
但是,当我通过创建静态函数(而不是在成员对象上调用成员方法)执行相同的操作时,应用程序的性能似乎很好,并且我不再看到上述日志消息。
基本上,调用静态函数是否应该比在本机代码中调用成员对象上的成员方法具有更好的性能? 更具体地说,是否完全存在于垃圾回收的 JNI 项目的本机代码中的成员对象或有限范围变量?GC中是否涉及C++调用栈?当涉及到 JNI 编程时,任何人都可以告诉我 C++ 内存管理如何满足 Java 内存管理吗?也就是说,如果我不在 Java 和 C++ 之间传递数据,我编写 C++ 代码的方式会影响 Java 内存管理(GC 还是其他方式)?
请允许我尝试举一个例子。忍受我,因为它太长了,如果你认为你有洞察力,欢迎你停止阅读这里。
我有几个对象。一个负责创建音频引擎、初始化输出等。它被称为 HelloAudioJNI(抱歉没有放可编译的示例,但代码很多)。
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 ...
因此,我还有几节课。WaveShaper 类如下所示:
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();
};
我们暂时不用担心 CAudioFilter 类,因为这个例子已经很长了。WaveShaper .cpp 文件如下所示:
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() {}
然后是 HelloAudioJNI.cpp。这就是我们进入问题的实质的地方。我在 init 函数中使用 new 正确创建了成员对象,因此:
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 ...
}
好的,到目前为止一切似乎都很好。然后在音频回调中,我们在成员对象上调用一些成员方法,如下所示:
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;
}
}
}
这就是产生频繁的音频中断和大量关于垃圾收集的消息的原因。但是,如果我将 CWaveShaper::processSample() 函数复制到回调上方的 HelloAudioJNI.cpp 并直接调用它而不是成员函数:
sample = waveShape(sample, coeff, 2);
然后我从我的 android 设备中获得了美妙的音频,并且我没有收到如此频繁的关于垃圾收集的消息。再一次的问题是,成员对象,还是完全存在于垃圾收集的 JNI 项目的本机代码中的有限范围变量?GC中是否涉及C++调用栈?当涉及到 JNI 编程时,任何人都可以告诉我 C++ 内存管理如何满足 Java 内存管理吗?也就是说,如果我不在 Java 和 C++ 之间传递数据,我编写 C++ 代码的方式会影响 Java 内存管理(GC 还是其他方式)?