java - एंड्रॉइड पर पार्सल होने के बाद सतह कचरा संग्रह कैसे नियंत्रित करता है?



android garbage-collection (1)

मैं इस सवाल के लिए एक संदर्भ के रूप में Surface.java के लिए स्रोत कोड का उपयोग कर रहा हूँ।

भूतल पार्सलेट योग्य इंटरफ़ेस लागू करता है, और यह देशी पक्ष पर ऑब्जेक्ट के लिए भी हैंडल रखता है।

मुझे यह जानने में दिलचस्पी है कि इस मामले में कचरा संग्रह कैसे नियंत्रित किया जाता है:

  1. एक सतह (ए) बनाया और एक पार्सल में लिखा है उसके बाद इसके बारे में कोई संदर्भ नहीं है

  2. मूल सतह (बी) की एक प्रति पार्सल से पढ़ी जाती है; मान लीजिए कि यह प्रस्तुतीकरण के लिए इस्तेमाल एक और धागा पर होता है। यह उदाहरण अब एक ही मूल संभाल पर (ए) धारण करता है और इस उदाहरण का एक मजबूत संदर्भ कहीं और है।

  3. एक जीसी होता है और (ए) एकत्र किया जाता है क्योंकि यह अब संदर्भित नहीं है। finalize() चलाया जाता है, जो release() कॉल करता है, जो बदले में नेटिव हैंडल के लिए देशी कॉल nativeRelease(long) कॉल करता है।

स्रोत कोड पर एक सरसरी नज़रिए मुझे लगता है कि अब (बी) को बाल्टी को किक करना चाहिए और देशी संभाल जारी होने के बाद से काम करना बंद करना चाहिए, लेकिन इसे दोहराने की कोशिश करने के बाद ऐसा नहीं लगता है। (ए) एकत्रित करता है, लेकिन (बी) पर रहता है और उपयोग करने योग्य रहता है।

अब मुझे लग रहा है कि कुछ संदर्भ गिनती देशी वस्तु के साथ चल रही है, या पार्सिंग प्रक्रिया के मूल पक्ष पर कुछ अन्य जादू है।

भले ही मेरी धारणा सही है या नहीं, मैं इस व्यवहार का कारण बनने पर अवलोकन के लिए देख रहा हूं, अधिमानतः ढांचे के स्रोत कोड के कुछ संदर्भों के साथ। मैं भी समानता में दिलचस्पी रहा हूँ कि सतह के लॉकिंग इसी तरह के मामलों में कैसे काम करता है।


सतहें बफर क़्यूयु में केवल संदर्भ हैं उनके पास निर्माता और रिसीवर के बीच ग्राफिकल बफ़र्स भेजने के लिए बातचीत करने के लिए एक बांधने वाली टोकन होती है। एक प्रासंगिक जेएनआई कोड :

static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz, jlong nativeObject, jobject parcelObj) {
  Parcel* parcel = parcelForJavaObject(env, parcelObj);
  if (parcel == NULL) {
    doThrowNPE(env);
    return 0;
  }

  android::view::Surface surfaceShim;

  // Calling code in Surface.java has already read the name of the Surface
  // from the Parcel
  surfaceShim.readFromParcel(parcel, /*nameAlreadyRead*/true);

  sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));

  // update the Surface only if the underlying IGraphicBufferProducer
  // has changed.
  if (self != nullptr
        && (IInterface::asBinder(self->getIGraphicBufferProducer()) ==
                IInterface::asBinder(surfaceShim.graphicBufferProducer))) {
      // same IGraphicBufferProducer, return ourselves
      return jlong(self.get());
  }

  sp<Surface> sur;
  if (surfaceShim.graphicBufferProducer != nullptr) {
    // we have a new IGraphicBufferProducer, create a new Surface for it
    sur = new Surface(surfaceShim.graphicBufferProducer, true);
    // and keep a reference before passing to java
    sur->incStrong(&sRefBaseOwner);
  }

  if (self != NULL) {
    // and loose the java reference to ourselves
    self->decStrong(&sRefBaseOwner);
  }

  return jlong(sur.get());
}

आप स्पष्ट रूप से देख सकते हैं, कैसे पार्सल से एक बाइंडर टोकन पढ़ा जाता है और IGraphicBufferProducer आईपीसी इंटरफ़ेस में परिवर्तित हो जाता है।

बांधने वाली टोकन कर्नेल में संदर्भ-गिने जाते हैं, उपयोगकर्ता के किसी भी स्पेस संदर्भ को नष्ट करते हुए अधिक से अधिक कुछ भी मौजूद नहीं होता है।

जब आप एक ही प्रक्रिया में होते हैं, तो लॉकिंग सिमेंटिक्स बदलते नहीं हैं, क्योंकि मूल Surface उदाहरणों की कैश रखता है :

sp<Surface> Surface::readFromParcel(const Parcel& data) {
  Mutex::Autolock _l(sCachedSurfacesLock);
  sp<IBinder> binder(data.readStrongBinder());
  sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote();
  if (surface == 0) {
   surface = new Surface(data, binder);
   sCachedSurfaces.add(binder, surface);
  } else {
    // The Surface was found in the cache, but we still should clear any
    // remaining data from the parcel.
    data.readStrongBinder();  // ISurfaceTexture
    data.readInt32();         // identity
  }
  if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) {
    surface = 0;
  }
  cleanCachedSurfacesLocked();
  return surface;
}

प्रत्येक जावा Surface उदाहरण, जो समान प्रक्रिया के भीतर पार्सिंग / अननसेलिंग के द्वारा बनाई गई है, उसी देशी Surface को संदर्भित करता है, जिसका अर्थ है कि ताले अभी भी प्रभावी होंगे: विवाद के मामले में आपको एक अपवाद प्राप्त होगा।

एक साथ कई प्रक्रियाओं से बेदखली सतहों को आकर्षित करने का प्रयास विफल हो सकता है क्योंकि IGraphicBufferProducer अनुबंध स्पष्ट रूप से मना करता है कि :

// connect attempts to connect a client API to the IGraphicBufferProducer.
// This must be called before any other IGraphicBufferProducer methods are
// called except for getAllocator.
//
// This method will fail if the connect was previously called on the
// IGraphicBufferProducer and no corresponding disconnect call was made.
//
// outWidth, outHeight and outTransform are filled with the default width
// and height of the window and current transform applied to buffers,
// respectively. The token needs to be any binder object that lives in the
// producer process -- it is solely used for obtaining a death notification
// when the producer is killed.
virtual status_t connect(const sp<IBinder>& token,
        int api, bool producerControlledByApp, QueueBufferOutput* output) = 0;

आप डिवाइस और फ़र्मवेयर निर्माता के लिए एंड्रॉइड वेबसाइट पर निचले-स्तरीय ग्राफ़िकल स्टैक आर्किटेक्चर के बारे में अधिक विवरण पा सकते हैं।





parcelable