Re: Memory consumption
- From: "Mark J. McGinty" <mmcginty@xxxxxxxxxxxxxxx>
- Date: Sat, 13 Oct 2007 06:33:32 -0700
"MFS" <noSpam@xxxxxxxxxxxxx> wrote in message
news:OD6B6PxCIHA.1056@xxxxxxxxxxxxxxxxxxxxxxx
I have problems with memory consumption by ado.recordset object.
The following code from MSDN developers center shows the same probleme,
meaning: if you start the application I would expect, that the memory
consumption shown by taskmanager is fixed on a certain value, but it is
increasing while the application is running.
Can anybody help me with same tipps what went wrong?
I noticed you're using IADORecordBinding in a console app... reminds me of
the diner scene in Pulp Fiction, "if you find my answers frightening you
should cease to ask scary questions." So I think I'll give this detail a
miss... well, just one comment: if for whatever reason you have something
against using the Fields collection, three words: get over it.
Next, this may not address the source of the cunsomption, but I'd bet my
grandma's dentures and her fanciest wig, that it will greatly decrease its
magnitude: instantiate, connect and open the ADO objects *outside* of the
for loop, either pass as parameters or declare at file scope. Doing all
that once, instead of 100,000 times, will surely make a big difference.
Hey, whoa, hold the phone... setting aside for a moment that you don't
specify a provider for your connection... let me get this straight:
1. You create and open a connection, create and open a recordset on a table.
2. You dump the field values of the first row.
3. You assign a date field to a constant value, and update the row in the
table.
4. You dump the field values again.
5. You requery the recordset, and restore the value of the date field.
6. You update the row again, and dump the field values yet again.
* repeat steps 1-6 100,000 times
I see no MoveNext call, so I must assume that either you are processing the
same row 100,000 times, or you are relying on a clustered index, changing
its key to alter the order, to implicitly float a different row to the top
when you creat and open a recordset on the same table, again.
The former would be merely a useless waste of time, cpu and bandwidth.
The latter would be unreliable, it would incur a mind-numbing amount of
needless overhead, would perform like a banana slug... actually, it would be
certifiably insane, for the simple reason that it might be mistakenly
considered functional.
Anyway... sorry... my mind: blown to bits...
Here's your game plan:
1. Get rid of *all* of that data binding crap, immediately if not sooner.
2. Use the Fields collection to get field values, e.g.,
// had to guess at your field names, but assuming these are right...
printf("full name: %s %s",
pRstEmployee->Fields->Item["fname"]->Value,
pRstEmployee->Fields->Item["lname"]->Value);
2.a. Use Fields collection to set values...
pRstEmployee->Fields->GetItem("hiredate")->PutValue(varDate);
pRstEmployee->Update();
3. You are connection to one database, on one server, so
instantiate your connection object one time, call Open
one time
4. You are accessing rows in one table, so you should
instantiate your recordset object one time, call it's Open
method one time
4.a. Even though you only opened the recordset one time,
if by some strange chance there are more than one rows
in the table, you can get to any of them. Check this out,
you'll never guess what it does:
pRstEmployee->MoveNext();
5. If you have a useful purpose for this code in mind, it would
be just grand if you could briefly share it
-Mark
modified code from: http://msdn2.microsoft.com/en-us/library/ms681557.aspx
#import "C:\Programme\Gemeinsame Dateien\System\Ado\msado15.dll"
no_namespace rename("EOF", "EndOfFile")
#include <oledb.h>
#include <stdio.h>
#include <conio.h>
#include "icrsint.h"
// class extracts only fname,lastname and hire_date from employee table
class CEmployeeRs : public CADORecordBinding {
BEGIN_ADO_BINDING(CEmployeeRs)
// Column fname is the 2nd field in the table
ADO_VARIABLE_LENGTH_ENTRY2(2, adVarChar, m_sze_fname,
sizeof(m_sze_fname), le_fnameStatus, FALSE)
// Column lname is the 4th field in the table.
ADO_VARIABLE_LENGTH_ENTRY2(4, adVarChar, m_sze_lname,
sizeof(m_sze_lname), le_lnameStatus, FALSE)
// Column hiredate is the 8th field in the table.
ADO_VARIABLE_LENGTH_ENTRY2(8, adDBDate,m_sze_hiredate,
sizeof(m_sze_hiredate), le_hiredateStatus, TRUE)
END_ADO_BINDING()
public:
CHAR m_sze_fname[21];
ULONG le_fnameStatus;
CHAR m_sze_lname[31];
ULONG le_lnameStatus;
DBDATE m_sze_hiredate;
ULONG le_hiredateStatus;
};
// Function declarations
inline void TESTHR(HRESULT x) { if FAILED(x) _com_issue_error(x); };
void OpenX();
void PrintProviderError(_ConnectionPtr pConnection);
void PrintComError(_com_error &e);
int main() {
if ( FAILED(::CoInitialize(NULL)) )
return -1;
for (int i=0;i<100000;i++)
{
OpenX();
}
::CoUninitialize();
}
void OpenX() {
// Define ADO object pointers. Initialize pointers on define.
// These are in the ADODB:: namespace
_RecordsetPtr pRstEmployee = NULL;
_ConnectionPtr pConnection = NULL;
// Define string variables.
_bstr_t strCnn(" Data Source='TEST'; Initial Catalog='pubs'; Integrated
Security='SSPI';");
IADORecordBinding *picRs = NULL; // Interface Pointer declared.
CEmployeeRs emprs; // C++ Class object
DBDATE varDate;
try {
// open connection and record set
TESTHR(pConnection.CreateInstance(__uuidof(Connection)));
pConnection->Open("TEST", "BPBDE", "2702", adConnectUnspecified);
TESTHR(pRstEmployee.CreateInstance(__uuidof(Recordset)));
pRstEmployee->Open("Employee", _variant_t((IDispatch *)pConnection,true),
adOpenKeyset, adLockOptimistic, adCmdTable);
// Open an IADORecordBinding interface pointer for Binding Recordset to a
class.
TESTHR(pRstEmployee->QueryInterface(__uuidof(IADORecordBinding),(LPVOID*)&picRs));
// Bind the Recordset to a C++ Class here.
TESTHR(picRs->BindToRecordset(&emprs));
// Assign first employee record's hire date to variable, then change hire
date.
varDate = emprs.m_sze_hiredate;
printf("Original data\n");
printf("\tName - Hire Date\n");
printf(" %s %s - %d/%d/%d\n\n",
emprs.le_fnameStatus == adFldOK ?
emprs.m_sze_fname : "<NULL>",
emprs.le_lnameStatus == adFldOK ?
emprs.m_sze_lname : "<NULL>",
emprs.le_hiredateStatus == adFldOK ?
emprs.m_sze_hiredate.month : 0,
emprs.le_hiredateStatus == adFldOK ?
emprs.m_sze_hiredate.day : 0,
emprs.le_hiredateStatus == adFldOK ?
emprs.m_sze_hiredate.year : 0);
emprs.m_sze_hiredate.year = 1900;
emprs.m_sze_hiredate.month = 1;
emprs.m_sze_hiredate.day = 1;
picRs->Update(&emprs);
printf("\nChanged data\n");
printf("\tName - Hire Date\n");
printf(" %s %s - %d/%d/%d\n\n",
emprs.le_fnameStatus == adFldOK ?
emprs.m_sze_fname : "<NULL>",
emprs.le_lnameStatus == adFldOK ?
emprs.m_sze_lname : "<NULL>",
emprs.le_hiredateStatus == adFldOK ?
emprs.m_sze_hiredate.month : 0,
emprs.le_hiredateStatus == adFldOK ?
emprs.m_sze_hiredate.day : 0,
emprs.le_hiredateStatus == adFldOK ?
emprs.m_sze_hiredate.year : 0);
// Requery Recordset and reset the hire date.
pRstEmployee->Requery(adOptionUnspecified);
// Open IADORecordBinding interface pointer for Binding Recordset to a
class.
TESTHR(pRstEmployee->QueryInterface(__uuidof(IADORecordBinding),
(LPVOID*)&picRs));
// Rebind the Recordset to a C++ Class here.
TESTHR(picRs->BindToRecordset(&emprs));
emprs.m_sze_hiredate = varDate;
picRs->Update(&emprs);
printf("\nData after reset\n");
printf("\tName - Hire Date\n");
printf(" %s %s - %d/%d/%d", emprs.le_fnameStatus == adFldOK ?
emprs.m_sze_fname : "<NULL>",
emprs.le_lnameStatus == adFldOK ?
emprs.m_sze_lname : "<NULL>",
emprs.le_hiredateStatus == adFldOK ?
emprs.m_sze_hiredate.month : 0,
emprs.le_hiredateStatus == adFldOK ?
emprs.m_sze_hiredate.day : 0,
emprs.le_hiredateStatus == adFldOK ?
emprs.m_sze_hiredate.year : 0);
}
catch(_com_error &e) {
// Display errors, if any. Pass a connection pointer accessed from the
Connection.
PrintProviderError(pConnection);
PrintComError(e);
}
// Clean up objects before exit.
if (pRstEmployee)
if (pRstEmployee->State == adStateOpen)
pRstEmployee->Close();
if (pConnection)
if (pConnection->State == adStateOpen)
pConnection->Close();
}
void PrintProviderError(_ConnectionPtr pConnection) {
// Print Provider Errors from Connection object.
// pErr is a record object in the Connection's Error collection.
ErrorPtr pErr = NULL;
if ( (pConnection->Errors->Count) > 0) {
long nCount = pConnection->Errors->Count;
// Collection ranges from 0 to nCount -1.
for ( long i = 0 ; i < nCount ; i++ ) {
pErr = pConnection->Errors->GetItem(i);
printf("\t Error number: %x\t%s", pErr->Number, pErr->Description);
}
}
}
void PrintComError(_com_error &e) {
_bstr_t bstrSource(e.Source());
_bstr_t bstrDescription(e.Description());
// Print COM errors.
printf("Error\n");
printf("\tCode = %08lx\n", e.Error());
printf("\tCode meaning = %s\n", e.ErrorMessage());
printf("\tSource = %s\n", (LPCSTR) bstrSource);
printf("\tDescription = %s\n", (LPCSTR) bstrDescription);
}
.
- References:
- Memory consumption
- From: MFS
- Memory consumption
- Prev by Date: Re: Create table
- Next by Date: Re: Error: "Byte array truncation to a length of 8000"
- Previous by thread: Memory consumption
- Next by thread: Re: Create table
- Index(es):
Relevant Pages
|
|