gpt4 book ai didi

java - Android JNI native 代码中的 C++ 对象是否调用垃圾回收?

转载 作者:可可西里 更新时间:2023-11-01 16:04:50 26 4
gpt4 key购买 nike

那么,我有一个概念性的问题。我一直在 Android 上使用 JNI 来做低级音频“东西”。我已经用 C/C++ 完成了大量的音频编码,所以我认为这不是什么大问题。我决定在我的“ native ”代码中使用 C++(因为谁不喜欢 OOP?)。我遇到的问题(对我来说)似乎是一个奇怪的问题:当我在 C++ 代码中创建一个用于处理音频的对象时,我从未将这个对象传递给 Java(也没有相反),调用这个对象的方法对象似乎经常调用垃圾收集。由于这是在音频回调中发生的,结果是音频断断续续,而且我经常收到如下消息:

WAIT_FOR_CONCURRENT_GC blocked 23ms

但是,当我通过创建静态函数(而不是调用 memeber 对象的成员方法)执行相同的操作时,应用程序的性能似乎很好,我不再看到上面的日志消息。

基本上,有什么理由调用静态函数应该比在 native 代码中调用成员对象的成员方法有更好的性能?更具体地说,是成员对象,还是完全存在的有限范围变量在涉及垃圾收集的 JNI 项目的 native 代码中? C++调用栈是否参与GC?关于 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 ...

因此,我还有几个类(class)。 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 项目的 native 代码中的有限范围变量? C++调用栈是否参与GC?关于 JNI 编程时 C++ 内存管理如何满足 Java 内存管理,有没有人能给我一些见解?也就是说,如果我不在 Java 和 C++ 之间传递数据,我编写 C++ 代码的方式是否会影响 Java 内存管理(GC 或其他方式)?

最佳答案

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

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

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

您可以使用 DDMS/ADT 的“分配跟踪器”功能来查看托管堆上最近创建的对象,以及它们是从哪里分配的。如果您在 GC 困惑期间运行它,您应该能够知道是什么导致了它。

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

关于java - Android JNI native 代码中的 C++ 对象是否调用垃圾回收?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16679944/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com