2013-05-21 21 views
9

Kavramsal bir sorum var. Düşük seviyeli ses "şeyler" yapmak amacıyla Android'de JNI ile çalışıyorum. C/C++ 'da çok sayıda ses kodlaması yaptım, bu yüzden bunun bir problem olmayacağını düşündüm. "Yerel" kodumda C++ kullanmaya karar verdim (çünkü OOP'u sevmeyen kimdir?). Karşılaştığım sorun (benim için) garip biri gibi görünüyor: C++ kodunda ses işlemek için bir nesne oluşturduğumda ve bu nesneyi asla Java'ya (ya da başka bir şekilde) iletemedim Nesne, çöp toplama işlemini sık sık çağırıyor gibi görünüyor. Bu iç ses geri aramalar oluyor beri, sonuç kekemelik ses, ve ben çizgisinde sık mesajları alıyorum: AncakAndroid JNI yerel kod C++ nesneleri çöp toplama çağırmak mı?

WAIT_FOR_CONCURRENT_GC blocked 23ms 

, ben üye yöntemleri statik işlevleri oluşturarak (ziyade çağırarak aynı işlemleri gerçekleştirirken bir memeber nesnesi) uygulamanın performansı iyi görünüyor ve artık yukarıdaki günlük mesajını göremiyorum. Temelde

, yerel kodda üyesi nesne üzerinde üye yöntemlerini çağırmaktan daha iyi performansa sahip olmalıdır statik işlevini çağırarak bir neden var mı? Daha spesifik olarak, çöp toplamada yer alan bir JNI projesinin yerel kodunda tamamen yer alan üye nesneler veya sınırlı kapsam değişkenleri var mı? C++ çağrı yığını GC'de yer alıyor mu? JNI programlama söz konusu olduğunda C++ bellek yönetiminin Java bellek yönetimi ile nasıl bir araya geldiğine dair herhangi bir fikir verilebilir mi? Yani, Java ve C++ arasında veri aktarmadığım durumda, C++ kodu yazdığım yol, Java bellek yönetimini (GC veya başka bir şekilde) etkiler mi?

bana bir örnek vermeye çalışacağım izin verin. Yanımda ol, çünkü bu çok uzun, ve eğer senin içgörün olduğunu düşündüğünüzde, burada okumayı bırakmaya davetlisiniz.

Ben nesnelerin bir çift var. Ses motorunun yaratılmasından, çıktının başlatılmasından vb. Sorumludur. Buna HelloAudioJNI denir (derleme yapabildiğimiz için özür dilerim, ama çok fazla kod var).

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 ... 

Bundan sonra birkaç tane daha dersim var. WaveShaper sınıfı şuna benzer:

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(); 
}; 

bu örnek zaten oldukça uzun olduğundan en şimdilik CAudioFilter sınıfının dert edelim.

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() {} 

ve sonra HelloAudioJNI.cpp var: WaveShaper .cpp dosyası bu gibi görünüyor. Meselenin etine girdiğimiz yer burası. Ben, böylelikle, init işlevi içinde yeni kullanılarak, düzgün üye nesneleri oluşturmak:

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 ... 
} 

Tamam herşey şu ana kadar iyi görünüyor. Sonra ses callback'inde içeride biz şöyle üyesi nesne üzerinde bazı üye yöntemleri çağırmak:

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; 
     } 
    } 
} 

Bu sık ses kesintileri ve çöp toplama ilgili çok sayıda mesaj üretir budur. Ancak, ben hemen geri arama yukarıda HelloAudioJNI.cpp için CWaveShaper :: processSample() fonksiyonunu kopyalayıp yerine doğrudan üye işlevi:

sample = waveShape(sample, coeff, 2); 

Sonra benim android çıkan güzel güzel ses elde cihaz ve çöp toplama hakkında sık sık mesaj alamıyorum.Bir kez daha, soruları, çöp toplamada yer alan bir JNI projesinin tamamen yerel kodunun içinde yaşayan üye nesneler veya sınırlı kapsam değişkenleridir. C++ çağrı yığını GC'de yer alıyor mu? JNI programlama söz konusu olduğunda C++ bellek yönetiminin Java bellek yönetimi ile nasıl bir araya geldiğine dair herhangi bir fikir verilebilir mi? Yani, Java ve C++ arasında veri aktarmadığım durumda, C++ kodu yazdığım yol, Java bellek yönetimini (GC veya başka bir şekilde) etkiler mi?

cevap

3

CHelloAudioJNI::init(...) depolar waveshaper bir yığın değişken (double coefficients[2]) için bir işaretçi. Katsayılar kapsam dışı bırakıldıktan sonra waveShaper->coeffs'a eriştiğinizde, BadThings (tm) olur.

CWaveShaper yapıcınızdaki dizinin bir kopyasını oluşturun (ve bunu yıkıcıda silmeyi unutmayın). Ya da std::array'u kullanın.

+0

Katsayılar okunacak ancak yazılmayacak bir şey gibi duyulduğundan, bu teğetsel olabilir. Dolayısıyla, erişilen değerler belirsiz olabilir, ancak yığın bozulmasının meydana geleceği hemen anlaşılmaz. –

+0

@ChrisStratton, yorumunuzu açıklayabilir misiniz? Ne demek istediğini anlamadım. – xaviersjs

+0

Burada kaldırılan sorun, artık ayrılan bellekten erişilen katsayıların değerlerinin yanlış olması nedeniyle düzeltmeniz gereken bir şeydir. Ancak, daha sonra katsayıları değiştirmeye çalışmadığınız sürece, bu yanlış bir program akışına neden olmaz, ki bu da bu cevabın dolaylı olarak sunduğu çöp toplama işleminizin tek açıklaması gibi görünüyor. Tüm girdiler için geçerli olan bir hesaplamada kullanılacak katsayıları sadece * * okuyorsanız, "BadThings (tm)" "yanlış hesaplama sonuçları" ile sınırlıdır. –

5

C++ nesneleri ve Dalvik'ın çöp toplama arasında hiçbir ilişki yoktur. Dalvik, kendi iç depolama alanı dışında, yerel yığının içeriğine hiç ilgi duymuyor. Java kaynaklarından oluşturulan tüm nesneler, çöp toplama işleminin gerçekleştirildiği "yönetilen" yığında yaşar.

Dalvik GC, yerel yığını değil; VM tarafından bilinen her bir thread, yorumlayıcının kullanması için ayrı bir desteye sahiptir.

C++ ve yönetilen nesnelerle ilgili tek yol, bir şekilde nesneleri eşleştirerek bir ilişki oluşturmayı seçerseniz (örneğin, bir C++ yapıcısından yeni bir yönetilen nesne oluşturma veya bir Java sonlandırıcıdan yerel bir nesneyi silme).

DDMS/ADT'nin "Ayırma İzleyicisi" özelliğini, yönetilen yığındaki en son oluşturulan nesneleri ve nerede ayrıldığını görmek için kullanabilirsiniz. GC flurry sırasında bunu çalıştırırsanız, ona neyin sebep olduğunu söyleyebilmeniz gerekir. Ayrıca, günlük künyesi iletileri, iletilerin uygulamanızdan geldiğinden emin olmak için denetlemeniz gereken işlem ve iş parçacığı kimliklerini (komut satırının kullanımı, adb logcat -v threadtime) ve ayrıca GC etkinliğini hangi iş parçacığı olduğunu görmenizi sağlar. gerçekleşiyor. İş parçacığı adlarını DDMS/ADT'deki "Konular" sekmesinde görebilirsiniz.