Announcement

Collapse
No announcement yet.

CCW Garbage Collection geht nicht

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • CCW Garbage Collection geht nicht

    Hallo.

    Eine "schönes" Interop Problem (ob das hier das richtige Forum ist?!?):

    Ich arbeite zur Zeit in einem C++ Projekt und soll externe Libraries ansprechen. Diese sind managed DLLs (vermutlich mit C# geschrieben) (mit einem kleinen unmanaged Teil). Wie vom Hersteller empfohlen spreche ich die Mittels COM an (Registriert mit RegAsm, tlbs importiert). Das wird also mit dem COM Callable Wrapper (CCW) ausgeführt

    Das C++ Projekt spricht aber auch mittels einem eigenen C++/CLI Wrapper eigene C# DLLs an. (Das C++ Projekt ist ein uraltes aber immer weiter gepflegtes Monster, in dem alle Sauereien gemacht wurden, die man machen kann - ach ja: es ist ein 32 Bit Projekt).

    Wenn ich jetzt mittels COM die externe DLLs aufrufe, habe ich ein Memory Leak im Managed Memory Teil (Native Memory ist OK). In einer Schleife ist das dann tödlich. Egal ob ich mit SmartPointer oder arbeite oder "manuell". Bei Manuell kann ich zumindest den Refcouter ansehen - und der ist nach dem Release Null. Vermutlich führt der CCW keine Garbage Collection aus.

    In einem "Minimalbeispiel-Projekt" klappt aber alles einwandfrei - selbst mit den Projektsettings -> es hängt also mit der Konfiguration zusammen.
    [highlight=c++]
    [..]
    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    [..]
    CComPtr<IID_YYY> ZZZ;
    HRESULT hr = CoCreateInstance(CLSID_XXX, NULL, CLSCTX_INPROC_SERVER, IID_YYY, (void**)&ZZZ);
    [..]
    for (..) {
    CComPtr<AAA> BBB = NULL;
    HRESULT res = ZZZ->Get_XYZ([..], &BBB);
    [..]
    }
    [..]
    [/highlight]

    Kann mir bitte einer einen Hinweis geben, wo ich noch suchen kann? Nach tagelangen Internet durchwühlen habe ich es jetzt aufgegeben

    Danke und Grüße
    Ralph Erdt

  • #2
    Du hast auf BBB in der Schleife auch Release (also kein delete oder sowas) aufgerufen wenn du das Ding nicht mehr brauchst?

    Comment


    • #3
      Das Beispiel ist mit SmartPointern. Das macht der Intern automatisch.

      Aber wenn ich es "manuell" mache, dann kommt das Problem (Memory Leak) auch. "refcnt" ist aber Null.
      [highlight=c++]
      for (..) {
      AAA* BBB = NULL;
      HRESULT res = ZZZ->Get_XYZ([..], &BBB);
      [..]
      ULONG refcnt = BBB->Release();
      }
      [/highlight]

      Comment


      • #4
        Wenn du an der Stelle kein Reference Counting brauchst sondern weißt das das komplett weg kann versuche mal BBB über Marshal.FinalReleaseComObject freizugeben.

        Comment


        • #5
          Danke für die Antwort, aber ich bin auf der C++ (unmanaged) Seite. Der externe Code ist managed, da komme ich nicht ran.

          Comment


          • #6
            OK, ich habe mal etwas geforscht, und nun das hier gebaut. Nachteil: Es geht nicht:

            [highlight=c++]
            for (..) {
            CComPtr<AAA> BBB = NULL;
            HRESULT res = ZZZ->Get_XYZ([..], &BBB);
            [..]
            ExecuteCcwGc(BBB);
            }
            [..]
            Im Wrapper: ExecuteCcwGc -> ExecuteCcwGcWrapper
            [..]
            void ExecuteCcwGcWrapper(void* ComObject) {
            IntPtr ptr(ComObject);
            Object^ obj = System::Runtime::InteropServices::Marshal::GetObje ctForIUnknown(ptr);
            try {
            System::Runtime::InteropServices::Marshal::FinalRe leaseComObject(obj);
            }
            catch (ArgumentException^ e) {
            System:iagnostics:ebug::WriteLine(e->Message);
            }
            catch (ArgumentNullException^ e) {
            System:iagnostics:ebug::WriteLine(e->Message);
            }
            catch (Exception^ e) {
            System:iagnostics:ebug::WriteLine(e->Message);
            }
            System::GC::Collect();
            }
            [/highlight]

            FinalReleaseComObject schmeißt "ArgumentException" -> Parameter ist kein COM Objekt. "Obj" sieht im Debugger aber sehr gut aus. Die Struktur, etc, alles da.

            Edit: Weder mit SmartPointern, noch "manuell", wobei bei SmartPointer das eigentliche Objekt übergeben wird.

            Comment


            • #7
              häääääääääääää?!?

              Ich habe es jetzt gelöst.. Gelöst?!? Ich rufe einfach nur "System::GC::GetTotalMemory(true);" im Wrapper auf, und schon wird der Speicher aufgeräumt. Alles andere hat nichts gebracht. WTF?

              Edit:
              Siehe https://stackoverflow.com/questions/...gettotalmemory

              Comment


              • #8
                Hallo,

                weil durch true ein GC erzwungen wird. Schau dir die Überladungen zu GC.Collect (und GC.WaitForPendingFinalizer) einmal dazu an.

                Mit einer kompletten GC sollte es auch möglich sein:
                [highlight=c#]
                GC.Collect(3, GCCollectionMode.Forced, true, true);
                GC.WaitForPendingFinalizers();
                GC.Collect(3, GCCollectionMode.Forced, true, true);
                [/highlight]
                (ist hier in C#, aber lässt sich ja leicht auf C++ übertragen)

                Wobei es i.d.R. besser ist dem GC nicht ins Handwerk zu pfuschen, außer es hilft nichts.

                mfG Gü
                "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

                Comment


                • #9
                  Danke! Das ist es! Ich bin nicht auf die Idee gekommen den GC zwei mal anzuwerfen..

                  Dein Code hat IMHO nur zwei kleinere Nachteile: Hard-Coded Generation und erst ab .Net 4.6 lauffähig. So geht es auch unter .Net 4.5 (mindestes):

                  [highlight=c++]
                  System::GC::Collect(System::GC::MaxGeneration, System::GCCollectionMode::Forced, true);
                  System::GC::WaitForPendingFinalizers();
                  System::GC::Collect(System::GC::MaxGeneration, System::GCCollectionMode::Forced, true);
                  [/highlight]

                  Und was das "ins Handwerk pfuschen" angeht:

                  MemoryLeak.png (Danach geht es nie mehr runter - erst bei einem "pskill.exe" ... )

                  Wenn der [zensiert] mal seine arbeit machen würde..
                  Zuletzt editiert von gfoidl; 19.09.2017, 10:45. Reason: Fullquote entfernt

                  Comment


                  • #10
                    Hallo,

                    GC::MaxGeneration kannte ich noch gar nicht, daher hart-kodiert. Danke für den Hinweis!

                    Die zwei GC sind nötig, da beim der Finalizer die "finalisierten" Objekte wiederum die GC-Queue steckt und diese dann beim 2. GC weggeräumt werden.
                    Das ist vermutlich auch die Ursache warum beim automatischen GC, also ohne manuelles zutun, der Speicher nicht freigegeben wird, da die Finalizer-Queue nicht weggeräumt wird.

                    mfG Gü
                    "Any fool can write code that a computer can understand. Good programmers write code that humans can understand". - Martin Fowler

                    Comment

                    Working...
                    X