Insufficient memory to run script

In Dynamics AX, you may sometimes meet the following error message:

Error executing code: Insufficient memory to run script.

Information about this exception can be found in an article on Technical Support Blog but it contains some inaccuracies and doesn’t cover some aspects. In this post, I want to describe better what causes this error and to add few more details.

How it works

You might expect that Insufficient memory to run script is related to free operation memory, but it’s not so. Dynamics AX artificially limits how much data can be saved to a variable at once, for example how long text can be saved to a variable of type str.

Let’s assume that the limit is set to 9 MB (configuration will be discussed later). The following code tries to load 10 MB of text, which raises the exception.

s = System.IO.File::ReadAllText(@'c:\10MB'); //Insufficient memory to run script

However, if you load the same content in two smaller blocks, it will be saved without problems. After executing the following code, the value of variable s is 10 MB, thus exceeding the defined limit (9MB).

str s, t;
t = System.IO.File::ReadAllText(@'c:\5MB');
s += t;
t = System.IO.File::ReadAllText(@'c:\5MB');
s += t;

But if you try to save the value of s to another variable, the error is thrown again.

t = s; //Insufficient memory to run script

The same is true also for assignments to method parameters.

void doNothing(str _parm){}
doNothing(s); //Insufficient memory to run script

Using this knowledge, it’s also easy to write a function to examine actual value of the limit. The following code sequentially adds data to a variable and calls method strLen(), which ultimately leads to raising the exception.

str s;
int len;
int unitSize = 512 * 1024;
 
while (true)
{
    s += strRep('x', unitSize);
    len = strlen(s);
 
    infolog.clear(0);
    info(strFmt("%1 characters, %2 MB", len, (len*2) / (1024 * 1024)));
}

It’s worth noting that you may unexpectedly get the same error when trying to set higher value of unitSize, even though it is deep under the allowed limit. Demonstratively:

s = strRep('x', (1024*1024)-2); //OK
s = strRep('x', (1024*1024)-1); //Insufficient memory to run script

The strRep() function simply uses its own limit, independent on setup. The same may apply also to other methods, so it’s always necessary to analyze exactly what call caused the error.

Default limit value

I used information documented above to roughly measure the default limit in Dynamics AX 2009 and 2012.

In AX 2012, the limit is set to 50 MB in both client and AOS.

In AX 2009, the relation between the configured limit and the allowed size of variable is not so clear, especially on AOS (a part of memory is probably reserved for something else). The limit is set to approximately 7 MB on both client and AOS; I don’t know it precisely. But it’s surely higher than 4 MB mentioned on Technical Support Blog.

Configuration

The limit of variables’ size can be configured in Windows registry – see details on Technical Support Blog. The identical technique works in Dynamics AX 2012 too.

What’s not described there is the possibility to define the limit in .axc file, which is very useful for shared configurations. For example, to set the limit to 15 MB, open the .axc in a text editor and add a line with text maxbuffersize,Text,15.

If you decide to set client and AOS limits differently, don’t forget that both limits (effectively the lower one) may apply to your code, if a variable’s value is used on both client and server. In general, you should avoid such calls, unless they’re really necessary, because they’ll be quite slow and will occupy the network band between client and AOS.

But if you know that you need to change the limit for one tier only, you can do that. You can for example set higher limit on an AOS dedicated for batch tasks.

What it is good for

I suppose that developers of Dynamics AX had their own reason to implement this limit, anyway it helps us to find places where memory is wasted. It’s often not necessary to load all data to memory; it’s possible to process them in smaller blocks, to save subresults to a file and so on.

For example, I saw a function that read data from database, build an XML string from them and finally saved it to a file. Unfortunately the resulting string exceeded the allowed limit, so it all ended with the message about insufficient memory. But in fact all what was needed was to save data to the file more frequently, let’s say in batches of 100 records. If it was critical to write the complete file or no file at all, I would save data to a temporary file and move the file to the its target destination at the end. Building the whole string in memory was simply wasting of resources.

Exactly for this detection it may be useful to decrease the limit in testing environments.

One Comment

Leave a Reply

Your email address will not be published.