VBScript Out Of Memory Error

2019-02-10 21:19发布

问题:

I have a classic ASP CRM that was built by a third party company. Currently, I have access to the source code and am able to make any changes required.

Randomly throughout the day, usually after some prolonged usage by users, most of my pages start getting an Out of Memory error.

The way that the application is built, is all the pages and scripts pull core functions from a Global.asp file. In that file are embeds to other global files as well, but the error presented shows

Out Of Memory

WhateverScriptYouTriedToRun.asp Line 0

Line 0 is the include for the global.asp file. Once the error occurs, after an unspecified amount of time the error occurence subsides for some time but then begins to reoccur again. With how the application is written, and the functions it uses, and the "diagnostics" I've already done - it seems to be a common used function that is withholding data such as recordset or something of that nature and then not releasing it properly. Other users then try to use the same function and eventually it just fills up causing the error. The only way for me to effectively clear the error is to actually restart IIS, Recycle the App Pool, and Restart the SQL Server Services.

Needless to say, myself and my users are getting annoyed....

I can't pinpoint the error due to the actual error message presented being Line 0 - but from there I have no idea where in the 20K lines of code it could be hanging up. Any thoughts or ideas on how to isolate or at least point me in the right direction to begin clearing this up? Is there a way for me to increase "memory" size for VBScript? I know there is a limitation but is it set at say...512K and you can increase it to 1GB?

Here are things I have tried:

  1. Removing SQL Inline statements into Views
  2. Going through several hundred scripts and ensuring that every OpenConnection & OpenRecordSet is followed by an appropriate Close.
  3. Going through the Global File and commenting out any large SQL statements such as ApplicationLog (A function that writes the executed query into a table).
  4. Some smaller script edits.

回答1:

Common Memory Leak

You say you are closing all recordsets and connections which is good.

But are you deleting objects?

For example:

Set adoCon = new
Set rsCommon = new

'Do query stuff

'You do this:
rsCommon.close
adocon.close

'But do you do this?
Set adoCon = nothing
Set rsCommon = nothing

No garbage collection in classic ASP, so any objects not destroyed will remain in memory.

Also, ensure your closes/nothings are run in every branch. For example:

adocon.open
rscommon.open etc

'Sql query
myData = rscommon("condition")

if(myData) then
  response.write("ok")
else
  response.redirect("error.asp")
end if

'close
rsCommon.close
adocon.close
Set adoCon = nothing
Set rsCommon = nothing

Nothing is closed/destroyed before the redirect so it will only empty memory some of the time as not all branches of logic lead to the proper memory clearance.

Better Design

Also unfortunately it sounds like the website wasn't designed well. I always structure my classic ASP as:

<%
    Option Explicit

    'Declare all vars
    Dim this
    Dim that

    'Open connections
    Set adoCon...
    adocon.open()

    'Fetch required data
    rscommon.open strSQL, adoCon
        this = rsCommon.getRows()
    rsCommon.close

    'Fetch something else
    rscommon.open strSQL, adoCon
        that = rsCommon.getRows()
    rsCommon.close

    'Close connections and drop objects
    adoCon.close
    set adoCon = nothing
    set rscommon = nothing

    'Process redirects
    if(condition) then
        response.redirect(url)
    end if
%>
<html>
<body>

<%
    'Use data
    for(i = 0 to ubound(this,2)
        response.write(this(0, i) & " " & this(1, i) & "<br />")
    next
%>

</body>

</html>

Hope some of this helped.



回答2:

Have you looked at using a memory monitoring tool to see how much memory fragmentation is happening? My guess at a possible cause is that some object of a size is trying to be created but there isn't enough room in the memory to store it as one contiguous chunk. Imagine needing room to store an object that would take 100 MB and while there may be several hundred megabytes free, the largest contiguous chunk is 90MB then this doesn't fit.


Debug Diagnostic Tool v1.1 would be a tool where Bernard's articles may help in understanding how to use the tool.

Another thought is the question of how much string concatenation is there in the code? I remember where I used to work had problems with doing a lot of string concatenation operations that sucked up memory that may be another idea to consider.


Yeah, I could see some shock at that kind of number the first few times you see it but then if you understand what the code is doing it may make sense for why so much space gets reserved right off the bat at times.


I haven't used that debug tool specifically but I did have a tool that took a snapshot of memory when pages were hung so I couldn't tell if there was a performance impact of the tool or not. Course in my case I used a similar tool in 2004 so it has been a few years since I've had to research this kind of issue.



回答3:

Just going to throw this in here, but this problem has taken a long time to solve. Here's a breakdown of what we did:

  1. We took all the inline SQL and made SQL Views, every SELECT statement is now handled with a VIEW first.

  2. I took every single SQL INSERT and UPDATE (as much as I could without breaking the system) and put them into Stored Procedures.

    #2 was the one item that really made the biggest difference

  3. Went through several thousand scripts, and ensured that variables were properly disposed of, and all the DB Open Connections were followed correctly with a Close Connection and same with Open/Close RecordSet.

  4. One of the slow killers was doing something like:

    ID = Request.QueryString("ID)

at the top of the page. Before redirecting, or closing a page, there is always a:

Set ID = Nothing 

or the complete removal of the inference.