Aufruf einer Webservicefunktion erhöht dauerhaft den Speicherverbrauch im Webserviceprogramm



  • Ich habe zwei Programme. Eins besitzt eine Webserviceklasse und das andere, das "Clientprogramm", ruft eine Funktion der Klasse auf. (Quellcode siehe unten.)

    Mein Problem: Wann immer das Clientprogramm die Webservicefunktion aufruft, erhöht sich (im Taskmanager) der benötigte Speicherplatz für das Webserviceprogramm, der nicht wieder freigegeben wird, auch nicht, wenn das Clientprogramm geschlossen wurde. Hier eine Auflistung:

    Am Anfang belegt das Webserviceprogramm 15840 KB.
    Beim ersten Aufruf vom Clientprogramm springt der Speicherverbrauch des Webserviceprogramms auf 19868 KB. Beim zweiten Aufruf auf 20104 KB, dann auf 20164 KB, 20176 KB und 20180 KB.

    Beide Programme sind als Releaseversion kompiliert. Das Clientprogramm wird nicht zweimal gleichzeitig aufgerufen, sondern ich warte immer jeweils, bis das Programm durch ist, bevor ich es erneut starte.

    Wieso belegt das Serviceprogramm immer mehr Speicher, der nicht wieder freigegeben wird? Bei größeren Programmen, wo auch noch Daten (Byte-Array mit 700 Einträgen) hin- und hergeschoben werden, kann der Speicher dann sogar auf 100 MB ansteigen.

    Hier die Projekte:

    Webservice (als simple Konsolenanwendung erstellbar):

    Program.cs:

    using System;
    using System.Net;
    using System.Windows.Forms;
    using Microsoft.Web.Services3.Addressing;
    using Microsoft.Web.Services3.Messaging;
    
    static class Program
    {
        static void Main()
        {
            EndpointReference endpointReference = new EndpointReference(
                new Uri("urn:MyService"),
                new UriBuilder("soap.tcp", IPAddress.Any.ToString(), 50000).Uri);
    
            SoapReceivers.Add(endpointReference, typeof(MyService));
    
            MessageBox.Show("Das Serviceprogramm wurde gestartet.");
    
            SoapReceivers.Clear();
        }
    }
    

    MyService.asmx:

    <%@ WebService Language="C#" CodeBehind="MyService.asmx.cs" Class="MyService" %>
    

    MyService.asmx.cs:

    using System.Web.Services;
    using System.Windows.Forms;
    
    [WebService(Namespace = "http://www.example.com/MyService")]
    public class MyService
    {
        [WebMethod]
        public void Function()
        {
            MessageBox.Show("Funktion wurde erreicht.");
        }
    }
    

    Clientprogramm:

    Program.cs:

    using System;
    
    class Program
    {
        static void Main()
        {
            MyServiceReference service = new MyServiceReference(
                "soap.tcp://" + Environment.MachineName + ":50000/MyService");
    
            service.Function();
        }
    }
    

    MyServiceReference.cs:

    using System;
    using System.Web.Services;
    using System.Web.Services.Description;
    using System.Web.Services.Protocols;
    using Microsoft.Web.Services3;
    using Microsoft.Web.Services3.Addressing;
    
    [WebServiceBinding(Namespace = "http://www.example.com/MyService")]
    public class MyServiceReference : WebServicesClientProtocol
    {
        public MyServiceReference(string url)
        {
            Destination = new EndpointReference(new Uri("urn:MyService"), new Uri(url));
        }
    
        [SoapDocumentMethod
            (RequestNamespace = "http://www.example.com/MyService",
             ResponseNamespace = "http://www.example.com/MyService",
             Use = SoapBindingUse.Literal,
             ParameterStyle = SoapParameterStyle.Wrapped)]
        public void Function()
        {
            Invoke("Function", new object[0]);
        }
    }
    


  • Ich denke, wenn der Speicher nicht wieder freigegeben wird, liegt es daran, dass du Objekte die Dispose() zur Verfügung stellen nicht auch disposed.

    Schau mal unter dem Gesichtspunkt durch deinen Code.



  • Das einzige, wo noch ein Dispose fehlen könnte, wäre in der "Program.cs" im Clientprogramm:

    using System;
    
    class Program
    {
        static void Main()
        {
            MyServiceReference service = new MyServiceReference(
                "soap.tcp://" + Environment.MachineName + ":50000/MyService");
    
            service.Function();
    
            service.Dispose();
        }
    }
    

    Allerdings bringt das nichts: Der Speicher im Webserviceprogramm geht weiterhin hoch und bleibt oben.

    Der von mir gepostete Quellcode reicht ja schon aus, um den Fehler zu rekonstruieren. Und in diesem Quellcode werden nur Variablen vom Typ EndpointReference, Uri, UriBuilder und object[] erstellt. Also nichts, was disposable wäre. Wenn ich also nicht irgendwas übersehen habe, müsste der Fehler vermutlich an etwas anderem statt an einem fehlenden Dispose liegen.



  • C# verwendet einen GarbageCollector. Das bedeutet, dass der Speicher nicht zwingend dann freigegeben wird, wenn er nicht mehr benötigt wird.

    So lange es durch den steigenden Ressourcenverbrauch nicht zu ernsthafte Problemen kommt würde nicht versuchen unnötigerweise zu optimieren.



  • Naja, in einem größeren Projekt steigt der Speicherverbrauch schon mal auf über 100 MB. Und das finde ich schon etwas bedenklich. Und da es eben an diesem WebService-Aufruf liegt, muss ich wissen, wie ich das ändern kann.



  • Hast du mal nen Profiler bemüht? Der kann dir zeigen wo der Speicher flöten geht, welche Funktionen, welche Klassen usw.



  • Ich bin nun wieder an dem Projekt dran und hatte auch einen Profiler verwendet, allerdins habe ich dort nichts gefunden, was irgendwie falsch läuft und worüber ich selbst die Kontrolle im Programm habe.

    Doch nun ist nochmal ein komplett neues Problem aufgetreten. Nicht im vorliegenden Testprogramm, aber im eigentlichen Projekt. Es ist eine sehr merkwürdige Sache und vielleicht kann mir jemand erklären, woran das liegt:

    Wenn das Programm frisch auf einen PC aufgespielt wird, muss ich mehrere Webservice-Calls ausführen, um den Speicherverbrauch hochzusetzen. Beispiel: Das Programm steht bei 20 MB und mit jedem Webservicecall kommt 1 MB hinzu. Ich mss also eine ganze Weile den Call aufrufen, um es auf zum Beispiel 50 MB hochzusetzen. Soweit das alte Problem.

    Doch nun der neue Effekt: Ich starte das Programm danach neu und es liegt erwartungsgemäß wieder bei 20 MB. Dann führe ich einen(!) Webservicecall aus und es springt sofort hoch auf 50 MB. So als hätte sich Windows gemerkt, wieviel Speicher beim letzten Mal am Ende angefordert war und setzt das Programm jetzt sofort auf diesen Wert hoch. Selbst wenn ich den PC komplett ausschalte und wieder hochfahre, wird er beim ersten Webservicecall sofort auf 50 MB springen. Benenne ich dagegen die Exe-Datei um, starte sie und führe einen Webservicecall aus, dann steigt der Speicher wieder in kleinen Schritten.

    Woran könnte das liegen? Hat da jemand eine Idee? Gibt es einen Registryeintrag, der sich den Speicherverbrauch von Programmen merkt, um dann beim nächsten Mal sofort diesen maximalen Wert mit einem Mal zu setzen?



  • Hört sich für mich so an, als ob irgendwo irgendetwas gecachet werden würde, was beim ersten Call geladen wird.



  • Wenn das so ist, dann ist es auf keinen Fall vom Programm selbst gesteuert. Denn im Programm gibt es, wie im obigen Beispiel, eben nur so ein Invoke. Es muss also etwas sein, was Windows oder das .NET-Framework steuert. Und deshalb würde ich eben gerne wissen: Gibt es da irgendwo etwas, was von Windows abgelegt wird, um sich zu merken, wieviel Speicher ein Programm verbraucht hat? Denn wie gesagt: Ich kann den Computer komplett aus- und wieder einschalten und das Phänomen tritt wieder auf. Es ist also keine reine RAM-Geschichte. Ich hab schon ausprobiert, vor und nach dem Start des Programms einen Snapshot zu machen, aber ich hab nicht wirklich was gefunden.


Anmelden zum Antworten