所以,我有一个概念性的问题。我一直在Android上使用JNI来处理低级音频“东西”。我已经用C / C ++完成了大量的音频编码,所以我认为这不是什么大问题。我决定在我的“原生”代码中使用C ++(因为谁不喜欢OOP?)。我遇到的问题似乎(对我来说)是一个奇怪的问题:当我在C ++代码中创建一个处理音频的对象时,我从未将此对象传递给Java(也没有其他方式),调用此方法对象似乎经常调用垃圾收集。由于这是在音频回调中发生的,结果是音频断断续续,我得到了频繁的消息:
WAIT_FOR_CONCURRENT_GC blocked 23ms
但是,当我通过创建静态函数(而不是在memeber对象上调用成员方法)执行相同的操作时,应用程序的性能似乎很好,我不再看到上面的日志消息。
基本上,调用静态函数应该比在本机代码中的成员对象上调用成员方法具有更好的性能吗?更具体地说,是成员对象,还是完全存在的有限范围变量在垃圾收集中涉及的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或其他方式)?
答案 0 :(得分:5)
C ++对象和Dalvik的垃圾收集之间没有任何关系。 Dalvik对本机堆的内容没有兴趣,除了它自己的内部存储。从Java源创建的所有对象都存在于&#34;托管&#34;堆,这是垃圾收集发生的地方。
Dalvik GC不检查本机堆栈; VM已知的每个线程都有一个单独的堆栈供解释器使用。
C ++和托管对象相关的唯一方法是,如果您选择通过以某种方式配对对象来创建关系(例如,从C ++构造函数创建新的托管对象,或从Java终结器中删除本机对象)。 / p>
您可以使用&#34;分配跟踪器&#34; DDMS / ADT的功能,可以查看托管堆上最近创建的对象,以及它们的分配位置。如果你在GC乱舞期间运行它,你应该能够分辨出导致它的原因。
此外,logcat消息显示进程和线程ID(来自命令行使用,adb logcat -v threadtime
),您应该检查以确保消息来自您的应用程序,并查看哪个线程GC活动正在进行。你可以在&#34;线程&#34;中找到线程名称。 DDMS / ADT中的选项卡。
答案 1 :(得分:4)
CHelloAudioJNI::init(...)
存储指向堆栈变量(double coefficients[2]
)的指针
波形成形。当系数超出范围后访问waveShaper->coeffs
时,会发生BadThings(tm)。
在CWaveShaper
构造函数中复制数组(并且不要忘记在析构函数中删除它)。或者使用std::array
。