Ressourcenprobleme mit Recordsets

In letzter Zeit werde ich wieder häufiger von Entwicklerkollegen angesprochen, die Fehler wie “Nicht genügend Systemressourcen” oder “Nicht genügend Speicherplatz zum Ausführen der Operation” erhalten, wenn sie mehrere Formulare mit Daten öffnen oder Code ausführen, der mit Recordsets arbeitet. Wenn sie dann über den Taskmanager die Ressourcen des Systems betrachten, stellen sie fest, dass es hier keinerlei Engpässe gibt. Wir schauen uns in diesem Beitrag an, wie dieser Fehler entstehen kann und welche Möglichkeiten es gibt, diesen zu beheben.

Fehlermeldungen zu erhalten, die nicht unmittelbar mit dem geschriebenen Code und eventuellen Syntax- oder Kompilierfehlern zusammenhängen, ist selten schön – erst recht nicht, wenn diese Fehler auf dem einen Rechner auftreten, auf dem anderen aber nicht oder zu anderen Zeitpunkten.

Wir schauen uns in diesem Beitrag einmal an, wie wir die beiden eingangs genannten Fehler reproduzieren können. Anschließend kümmern wir uns um mögliche Lösungen für diese Fehler.

Fehler “Nicht genügend Systemressourcen” reproduzieren

Um diesen Fehler zu reproduzieren, erzeugen wir eine ausreichend große Menge von Recordset-Objekten. Um diese ohne viel Aufwand im Speicher zu halten, definieren wir ein Klassenmodul, das lediglich den Verweis auf das geöffnete Recordset-Objekt aufnehmen soll. Der Code dieses Klassenmoduls, das wir clsRecordsetWrapper nennen wollen, sieht wie folgt aus:

Private m_Recordset As Recordset
Public Property Set Recordset(rst As DAO.Recordset)
     Set m_Recordset = rst
End Property

Es enthält also lediglich eine private Variable, die den Verweis auf ein Recordset-Objekt speichern soll. Um dieses an die Klasse zu übermitteln, haben wir dieser außerdem die Property Set-Prozedur Recordset zugewiesen, der wir als Parameter das zu speichernde Recordset übergeben.

Diese Prozedur (siehe Listing 1) deklariert eine Collection-Variable zum Speichern der Instanzen der Wrapper-Klasse, Variablen zum Speichern eines Verweises auf das Database-Objekt der aktuellen Datenbank und des zu öffnenden Recordsets sowie für eine Instanz der Klasse clsRecordsetwrapper. Außerdem instanziiert die Prozedur ein neues Collection-Objekt und referenziert es mit der Variablen col.

Public Sub Recordsets()
     Dim col As Collection
     Dim i As Long
     Dim db As DAO.Database
     Dim rst As DAO.Recordset
     Dim objRecordsetWrapper As clsRecordsetwrapper
     Set db = CurrentDb
     Set col = New Collection
     For i = 1 To 10000
         Set objRecordsetWrapper = New clsRecordsetwrapper
         With objRecordsetWrapper
             On Error Resume Next
             Set .Recordset = db.OpenRecordset("tblVieleKunden", dbOpenDynaset)
             If Not Err.Number = 0 Then
                 MsgBox "Fehler: " & Err.Number & vbCrLf & vbCrLf & "Beschreibung: " & Err.Description _
                     & vbCrLf & vbCrLf & "Anzahl Recordsets: " & i
                 Exit Sub
             End If
             On Error GoTo 0
         End With
         col.Add objRecordsetWrapper
         Debug.Print i
     Next i
End Sub

Listing 1: Prozedur zum Erstellen einiger Recordsets und zum Speichern dieser in einer Wrapper-Klasse inklusive Fehlerbehandlung

Die folgende For…Next-Schleife wird maximal 10.000 Mal durchlaufen. Es ist allerdings davon auszugehen, dass unter der Standardkonfiguration normale Rechner nach ein paar hundert Iterationen aufgeben. Innerhalb der For…Next-Schleife erstellen wir ein neues Objekt auf Basis der Klasse clsRecordsetwrapper und weisen es der Variablen objRecordsetWrapper zu.

Dann weisen wir bei deaktivierter Fehlerbehandlung der Eigenschaft Recordsets dieser Klasse ein mit OpenRecordset neu geöffnetes Recordset auf Basis einer Beispieltabelle zu. Dies löst ab einer bestimmten Menge erstellter Recordsets den oben genannten Fehler aus. Ist das der Fall (oder tritt ein anderer Fehler auf), wollen wir diesen in einer Meldung ausgeben. Neben der Fehlernummer und der Fehlermeldung gibt die Meldung noch die Anzahl der geöffneten Recordsets aus. Nach der Anzeige der Fehlermeldung wird die Prozedur mit Exit Sub beendet.

Damit die Recordsets in der Wrapper-Klasse im Speicher verweilen und so erst dafür sorgen, dass der Fehler ausgelöst wird, fügen wir diese mit der Add-Methode zu der Collection namens col hinzu.

Rufen wir diese Prozedur nun auf, erscheint nach einigen bis etlichen Durchläufen der Schleife die Fehlermeldung aus Bild 1 (hier haben wir die Prozedur von einer Schaltfläche aus aufgerufen). Mit einem Klick auf OK wird die Prozedur beendet und die durch die Recordset-Variablen gebundenen Ressourcen werden wieder freigegeben. Der Fehler ist reproduzierbar und zeigt, dass in Abhängigkeit von der aktuellen Konfiguration des Systems und vermutlich auch abhängig von der aktuellen Auslastung mehr oder weniger Recordset-Objekte erstellt werden können.

Anzeige der Fehlermeldung nach dem Öffnen von 305 Recordsets

Bild 1: Anzeige der Fehlermeldung nach dem Öffnen von 305 Recordsets

Fehler “Nicht genügend Speicherplatz zum Ausführen der Operation” reproduzieren

Diesen Fehler konnten wir reproduzieren, indem wir einige Formularinstanzen aufgerufen haben. Da wir keine Lust hatten, viele verschiedene Formulare zu erstellen und diese zu öffnen, haben wir zu Beispielzwecken von einer Technik Gebrauch gemacht, die sonst zur Anzeige mehrerer Instanzen von Formularen mit verschiedenen Datensätzen verwendet wird.

Das Formular ist ein einfaches Formular, das wir über die Eigenschaft Datensatzquelle an die Tabelle tblVieleKunden gebunden haben (siehe Bild 2). Außerdem haben wir die Eigenschaft Enthält Modul im Bereich Andere des Eigenschaftenblatts auf Ja eingestellt. Das ist nötig, damit wir mehrere Instanzen des Formulars erstellen können.

Das an eine Tabelle gebundene Formular

Bild 2: Das an eine Tabelle gebundene Formular

Die Prozedur finden Sie in Listing 2. Hier deklarieren wir eine Variable für ein Collection-Objekt und eines zum Erfassen des aktuellen Formulars. Dabei verwenden wir gleich den Typ des Klassenmoduls dieses Formulars, in diesem Fall Form_frmRecordset.

Public Sub VieleFormulareMitRecordsetOeffnen()
     Dim col As Collection
     Dim i As Long
     Dim frm As Form_frmRecordset
     Set col = New Collection
     For i = 1 To 10000
         On Error Resume Next
         Set frm = New Form_frmRecordset
         If Not Err.Number = 0 Then
             MsgBox "Fehler: " & Err.Number & vbCrLf & vbCrLf & "Beschreibung: " & Err.Description _
                 & vbCrLf & vbCrLf & "Anzahl Formulare: " & i
             Exit Sub
         End If
         On Error GoTo 0
         col.Add frm
         Debug.Print i
     Next i
End Sub

Sie haben das Ende des frei verfügbaren Textes erreicht. Möchten Sie ...

TestzugangOder bist Du bereits Abonnent? Dann logge Dich gleich hier ein. Die Zugangsdaten findest Du entweder in der aktuellen Print-Ausgabe auf Seite U2 oder beim Online-Abo in der E-Mail, die Du als Abonnent regelmäßig erhältst:

Schreibe einen Kommentar