2016-06-24 7 views
2

Ich verwende das Objekt IBM.Data.DB2.DB2DataAdapter, um mehrere Verbindungen zu verschiedenen Datenbanken auf verschiedenen Servern herzustellen.
Meine grundlegende Schleife und Verbindungsstruktur sieht wie folgt aus:DB2Connection-Objektschleife Öffnen und dann Speicherausnahmen schließen

foreach (MyDBObject db in allDBs) 
{ 
    //Database Call here for current DB...//Get SQL, then pass it to DB call 
    QueryCurrentDB(command); 
} 

Then ...

DB2Connection _connection;  

Public DataTable QueryCurrentDB(DB2Command command) 
{ 

    _connection = new DB2Connection(); 
    DB2DataAdapter adapter = new DB2DataAdapter(); 

    _connection.ConnectionString = string.Format("Server={0};Database={1};UID={2};PWD={3};", _currentDB.DBServer, _currentDB.DBName, _currentDB.UserCode, _currentDB.Password); 
    command.CommandTimeout = 20; 
    command.Connection = _connection; 
    adapter.SelectCommand = command;     
    _connection.Open(); 

    adapter.Fill(dataTable); 

    _connection.Close(); 
    _connection.Dispose(); 

    return dataTable; 
} 

Wenn ich etwa 20 oder so Datenbanken auf verschiedenen Servern habe ich diese Ausnahme schließlich am Ende immer. Ich kann die Speicherzuordnung für jede Datenbankinstanz auch nicht steuern.

FEHLER [57019] [IBM] SQL1084C Der Datenbankmanager konnte keinen freigegebenen Speicher zuordnen, da ein Betriebssystemkernelspeicherlimit erreicht wurde. SQLSTATE = 57019

Der einzige Weg, ich in der Lage gewesen, dies zu umgehen ist vor jedem DB-Aufruf einen Thread schlafen legen, wie zum Beispiel:

System.Threading.Thread.Sleep(3000); 

ich hasse, würde irgendwelche Vorschläge geschätzt.

+0

Der Code gibt kein klares Bild (der 'sqlCommand' übergeben wird nicht verwendet und wie/wo' _connection' erstellt wird, wird nicht angezeigt). Die Fehlermeldung weist jedoch nicht ausdrücklich auf DBConnection hin. DBProvider-Objekte müssen entsorgt werden und es sieht nicht so aus, als wären Sie es. – Plutonix

+0

Ich habe den Code aktualisiert, um besser zu zeigen, wie ich die db2-Objekte verwende. – Airborne

Antwort

3

Im Code geschrieben, der Connection, Command und DataAdapter sind alle IDisposable anzeigt, dass sie entsorgt werden müssen zugewiesenen Ressourcen freizugeben. Aber nur das DBConnection Objekt ist tatsächlich entsorgt. Insbesondere in einer Schleife, wie Sie sie haben, ist es wichtig, diese zu entsorgen, um Lecks zu vermeiden.

Ich habe nicht die DB2-Anbieter, aber sie alle arbeiten ziemlich genau das gleiche, besonders in dieser Hinsicht. Ich würde mit dem Refactoring beginnen, beginnend mit MyDBObject. Anstatt nur Halten an Verbindungszeichenfolge params, haben sie die Verbindung (en) für Sie erstellen:

class MyDBObject 
{ 
    private const string fmt = "Server={0};Database={1};UID={2};PWD={3};"; 
    ... 
    public DB2Connection GetConnection() 
    { 
     return new DB2Connection(string.Format(fmt, 
        DBServer,DBName,UserCode,Password)); 
    } 
} 

Dann wird die Schleife Methode:

// this also could be a method in MyDbObject 
public DataTable QueryCurrentDB(string SQL) 
{ 
    DataTable dt = new DataTable(); 
    using (DB2Connection dbcon = currentDB.GetConnection()) 
    using (DB2Command cmd = new DB2Command(SQL, dbcon)) 
    { 
     cmd.CommandTimeout = 20; 
     dbcon.Open(); 
     dt.Load(cmd.ExecuteReader()); 
    } 
    return dt; 
} 
  • Am wichtigsten ist, beachten Sie, dass die IDisposable Objekte alles in einem using Block eingeschlossen. Dies wird das Ziel entsorgen (und schließen) und freigegebene Ressourcen freigeben.
  • Sie brauchen keine DataAdapter, um eine Tabelle zu füllen. Wenn man es auslässt, bedeutet das, dass man weniger IDisposable erstellt hat.
  • Anstatt den Befehl zu übergeben, übergeben Sie den SQL. Auf diese Weise können Sie auch das Objekt DBCommand erstellen, verwenden und entsorgen.
  • Wenn die Möglichkeit besteht, dass 2 Tabellen in der gleichen Datenbank abgefragt werden, würde ich einen weiteren Refactor erstellen, um es zu ermöglichen, beide Tabellen auf der gleichen Verbindung zu füllen.

Vorher: 2 von 3 Objekte wurden nicht (! pro Iteration) angeordnet ist
Nach: 2 von 2 Objekten angeordnet sind.

Ich vermute, der Täter war das DBCommand Objekt (ähnlich this question), aber es könnte eine Kombination von ihnen sein.

Den Thread in den Ruhezustand versetzen (wahrscheinlich), funktioniert, weil er GC die Möglichkeit gibt, die Bereinigung nachzuholen. Sie sind wahrscheinlich noch nicht aus dem Wald.Der obige Link stieß bei 400 Iterationen auf Probleme; 20 oder sogar 40 (20 * 2 Objekte) scheint eine sehr kleine Anzahl an Ressourcen zu erschöpfen.

Also ich vermute, dass andere Teile des Codes auch nicht ordnungsgemäß entsorgen und diese Schleife ist nur der Strohhalm, der das Kamel zurück bricht. Suchen Sie nach anderen Schleifen und DB-Objekten, die verwendet werden, und stellen Sie sicher, dass Sie sie löschen. Grundsätzlich sollte alles, was eine Dispose() Methode hat, in einem using Block verwendet werden.

+0

Ich werde meinen Code ändern und Ihre Vorschläge irgendwann heute testen. Ich werde dich wissen lassen, wie es geht, danke. – Airborne

+0

Ich wickelte alle Db-Objekte in die Verwendung von Anweisungen und mit meinen mehr als 20 verschiedenen Db-Verbindungen und Abfragen funktioniert es bisher sehr gut ohne Thread zu schlafen. Vielen Dank! – Airborne

+0

Gut zu hören. Beachten Sie, dass der Code in der Frage nur das Problem offenbarte. Ich würde denken, dass die App es sich leisten könnte, diese 20-40 Objekte zu lehnen, so dass Code woanders wahrscheinlich auch undicht ist. Der wirkliche Schlüssel ist der letzte Satz in der Antwort: * "alles, was eine' Dispose() 'Methode hat, sollte in einem Verwendungsblock verwendet werden" * – Plutonix

Verwandte Themen