2017-11-15 2 views
0

Ich debugge einen vorhandenen Windows-Dienst (geschrieben in C#), der alle paar Monate manuell neu gestartet werden muss, da es Speicher speichert.Windows-Dienst mit NHibernate erhöht benutzter Speicher

Der Service ist nicht sehr kompliziert. Er fordert eine JSON-Datei von einem externen Server an, der Produkte enthält. Als nächstes analysiert es diese JSON-Datei in eine Liste von Produkten. Für jedes dieser Produkte wird geprüft, ob dieses Produkt bereits in der Datenbank vorhanden ist. Wenn nicht, wird es hinzugefügt, wenn es existiert, werden die Eigenschaften aktualisiert.

Die Datenbank ist eine PostgreSQL-Datenbank und wir verwenden NHibernate v3.2.0 als ORM.

Ich habe JetBrains DotMemory wurde mit dem Service profilieren, wenn es läuft: DotMemory overvies

der Dienst gestartet wird und nach 30s es seine Arbeit beginnt zu tun. SnapShot # 1 wird vor dem ersten Lauf erstellt. Snapshot # 6 wurde nach dem 5. Lauf erstellt. Die anderen Snapshots werden auch nach einem Lauf erstellt. Wie Sie nach jedem Lauf sehen können, erhöht sich die Anzahl der Objekte um ca. 60k und der verwendete Speicher steigt mit ein paar MB nach jedem Lauf.

Bei genauerer Betrachtung Snapshot # 6, zeigt die beibehaltene Größe meist von NHibernate Sitzungsobjekte verwendet wird:

Snapshot #6

Hier ist mein OnStart Code:

try 
{ 
    // Trying to fix certificate errors: 
    ServicePointManager.ServerCertificateValidationCallback += delegate 
    { 
     _logger.Debug("Cert validation work around"); 
     return true; 
    }; 

    _timer = new Timer(_interval) 
    { 
     AutoReset = false // makes it fire only once, restart when work is done to prevent multiple runs 
    }; 
    _timer.Elapsed += DoServiceWork; 
    _timer.Start(); 
} 
catch (Exception ex) 
{ 
    _logger.Error("Exception in OnStart: " + ex.Message, ex); 
} 

Und mein DoServiceWork:

try 
{ 
    // Call execute 
    var processor = new SAPProductProcessor(); 
    processor.Execute(); 
} 
catch (Exception ex) 
{ 
    _logger.Error("Error in DoServiceWork", ex); 
} 
finally 
{ 
    // Next round: 
    _timer.Start(); 
} 

In SAPProductProcessor Ich verwende zwei db-Aufrufe Beide in einer Schleife. I Schleife durch alle Produkte aus der JSON-Datei und prüfen, ob das Produkt bereits in der Tabelle ist den Produktcode:

ProductDto dto; 
using (var session = SessionFactory.OpenSession()) 
{ 
    using (var transaction = session.BeginTransaction(IsolationLevel.ReadCommitted)) 
    { 
     var criteria = session.CreateCriteria<ProductDto>(); 
     criteria.Add(Restrictions.Eq("Code", code)); 
     dto = criteria.UniqueResult<ProductDto>(); 
     transaction.Commit(); 
    } 
} 
return dto; 

Und wenn der productDto I speichert sie aktualisiert werden:

using (var session = SessionFactory.OpenSession()) 
{ 
    using (var transaction = session.BeginTransaction(IsolationLevel.ReadCommitted)) 
    { 
     session.SaveOrUpdate(item); 
     transaction.Commit(); 
    } 
} 

Ich bin nicht sicher, wie man den Code oben ändert, um den Speicher und die Anzahl der Objekte zu erhöhen.

Ich habe bereits versucht var session = SessionFactory.GetCurrentSession(); anstelle von using (var session = SessionFactory.OpenSession()), aber das hat nicht die Erhöhung des Speichers zu stoppen.

aktualisieren

Im Konstruktor meiner Datenzugriffsklasse MultiSessionFactoryProvider sessionFactoryProvider injiziert. Und die Basisklasse wird mit : base(sessionFactoryProvider.GetFactory("data")) aufgerufen. Diese Basisklasse hat eine Methode BeginSession:

ISession session = _sessionFactory.GetCurrentSession(); 
if (session == null) 
{ 
    session = _sessionFactory.OpenSession(); 
    ThreadLocalSessionContext.Bind(session); 
} 

Und ein EndSession:

ISession session = ThreadLocalSessionContext.Unbind(_sessionFactory); 
if (session != null) 
{ 
    session.Close(); 
} 

In meiner Datenzugriffsklasse I base.BeginSession zu Beginn anrufen und base.EndSession an dann zu beenden.

+0

ist Ihr 'DoServiceWork' ein Singleton? Ich sehe, dass "SAPProductProcessor" jedes Mal erstellt wird. Vielleicht ist das etwas zum Anschauen? –

+2

Die SessionFactory ist eine Eigenschaft? Wo wird die SessionFactory erstellt? Es sollte nur einmal für einen Prozess (oder AppDomain) erstellt werden. –

+0

Öffnen Sie den Vergleich, öffnen Sie alle neuen Objekte und schauen Sie, welche Objekte gehalten werden und warum. –

Antwort

0

Der Vorschlag über den Singleton ließ mich meine Datenzugriffsklasse näher betrachten.

Ich dachte, beim Erstellen dieser Klasse mit jedem Lauf würde den NHibernate-Speicher freigeben, wenn es außerhalb des Geltungsbereichs läuft. Ich habe sogar etwas dispose call im Destruktor der Klasse hinzugefügt. Aber das hat nicht funktioniert, oder wahrscheinlicher mache ich es nicht richtig. Ich speichere jetzt meine Datenzugriffsklasse in einem statischen Feld und verwende sie erneut. Jetzt nimmt mein Speicher nicht mehr zu und die Anzahl der offenen Objekte bleibt gleich. Ich benutze den Dienst nur noch über DotMemory für mehr als eine Stunde und rufe den Lauf 150 Mal auf. Der Speicher des letzten Snapshots ist immer noch ungefähr 105 MB und die Anzahl der Objekte ist immer noch 117k und mein SessionFactory-Wörterbuch ist jetzt nur 4MB anstatt 150 * 4MB .

Verwandte Themen