10

所以,我有一个概念性的问题。我一直在使用 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 还是其他方式)?

4

2 回答 2

5

C++ 对象和 Dalvik 的垃圾回收没有关系。Dalvik 对本机堆的内容不感兴趣,除了它自己的内部存储。从 Java 源创建的所有对象都存在于“托管”堆上,这是进行垃圾收集的地方。

Dalvik GC 不检查本机堆栈;VM 已知的每个线程都有一个单独的堆栈供解释器使用。

C++ 和托管对象相关的唯一方式是,如果您选择通过以某种方式配对对象来创建关系(例如,从 C++ 构造函数创建新的托管对象,或从 Java 终结器中删除本机对象)。

您可以使用 DDMS / ADT 的“分配跟踪器”功能来查看托管堆上最近创建的对象,以及它们被分配的位置。如果你在 GC 乱跑期间运行它,你应该能够知道是什么原因造成的。

此外,logcat 消息显示进程和线程 ID(来自命令行使用adb logcat -v threadtime),您应该检查以确保消息来自您的应用程序,并查看 GC 活动发生在哪个线程上。您可以在 DDMS / ADT 的“线程”选项卡中查看线程名称。

于 2013-05-28T21:00:01.167 回答
4

CHelloAudioJNI::init(...)double coefficients[2]在 waveShaper 中存储指向堆栈变量 ( ) 的指针。当您waveShaper->coeffs在系数超出范围后访问时,会发生 BadThings(tm)。

CWaveShaper构造函数中复制数组(不要忘记在析构函数中删除它)。或使用std::array.

于 2013-05-22T01:42:01.863 回答