Ask Reuben – June 24, 2025
fglrun Memory Consumption
Do I have to care about memory consumption?
How to reduce memory consumption?
As a general rule of thumb, Genero BDL developers do not have to give a great deal of thought to memory allocation and deallocation. Genero BDL developers can concentrate on business logic and allow the runtime to manage memory for them. There are some good practises you can follow that will reduce the footprint of your Genero application.
Data Type Choice
Save memory by using STRING variables. When you use a CHAR / VARCHAR variable in your application, the runtime will allocate enough memory for the defined length. For instance CHAR(1024) will allocate 1024 bytes. When you use STRING it will only allocate memory for that STRING variable as needed, for example a value of “Hello” will only allocate 5 bytes.
Just as you can save memory by using STRING variables, you can also save memory by using DYNAMIC ARRAY. Memory for a DYNAMIC ARRAY is consumed as elements are added to the array as needed whilst static arrays the memory as determined by the array length is consumed as the array is defined.
For both CHAR and static array my coding rule is simple, you have to be able to explain the number used in the definition in order to justify its use. So …
DEFINE barcode CHAR(13)
… might be justified if the barcode is EAN-13 and so has 13 digits/characters.
DEFINE sales_data ARRAY[12] OF ...
… might be justified if this is sales per calendar month. But where I see
DEFINE where_part CHAR(1000) DEFINE sales_data ARRAY[500] OF ...
… then it can normally be concluded that the number used is just an arbitrarily large number and this code is wasting memory unnecessarily.
Pass by Reference, not Pass by Value
When passing CHAR variables to a function, all the defined length is pushed onto the stack. A CHAR(1024) will push 1024 bytes onto the stack. With a STRING, only the used amount is pushed onto the stack. For instance if a string has value “1=1” then 3 characters are pushed onto the stack, if you have a STRING representing a large body of text, lets say 10,000+ characters then 10,000+ characters are pushed onto the stack. When doing a lot of manipulation with STRING variables, consider using base.StringBuffer. When using base.StringBuffer object, only the reference to the base.StringBuffer object is placed onto the stack.
Dynamic Arrays also have the property that they are passed by reference, not by value.
The INOUT keyword can also be used to pass by reference, and not by value.
Split Modules
There is a misconception that a .42r is a program executable. It is not, it contains a mapping of functions to the .42m object file that they are in. A .42m is loaded as required so it can be advantageous to split your Genero application into several individual .4gl files and hence .42m files, and save memory by splitting modules. That way the static memory of a .42m is consumed only as it is loaded.
Prevent Large Arrays
The DYNAMIC ARRAY has unlimited length. You can build into your code protections against abnormally large lengths for the array.
This can be achieved by using ON FILL BUFFER and Page Mode DISPLAY ARRAY so that you only fetch into memory the visible rows of a DISPLAY ARRAY.
Alternatively in a FOREACH you could stop after reading X rows, or use a row limiting clause in the SQL statement, or if a where clause is generated from a CONSTRUCT, use AFTER FIELD, AFTER CONSTRUCT to ensure that the where clause generated is not “1=1” by forcing the user to enter some QBE criteria.
Stream
Both the XML and JSON functionality within Genero have the ability to stream. This means that for large JSON and XML documents, rather than holding the entire JSON and XML documents in memory, you can stream the document and only hold part of it in memory at any one time.
For XML, note the DOM classes versus the Streaming Stax classes.
For JSON, note the util.JSON class versus the streaming JSON classes.
Sledgehammers
There is an English saying, don’t use a sledge hammer to crack a nut. There are two instances where you may end up using some powerful Genero functionality to solve a small problem.
A Web Component instantiates a Web Browser in a Genero form. If you have multiple Web Components in a Genero form then you are instantiating multiple Web Browsers. A good rule of thumb is only one Web Component per form.
IMPORT JAVA instantiates a JVM. Rather than having every running Genero program instantiate its own JVM because you use IMPORT JAVA in a library function, consider implementing the Java call via a Web Service. That way the number of JVM instantiated will be small.
Split or Stop and Start Again
Over the lifetime of a Genero application, the fglrun process will hold onto physical memory allocated so that it can be used again by the Genero application without having to ask the operating system for more memory. You will not typically see the memory used by an individual fglrun process decrease and there is no command to tell it it to unallocate memory that is not being used. So if a program has asked for and used memory, it will hold onto it.
This works well when a Genero application is doing a small task and has a small lifetime, lets say 5-15 minutes. There are two practices that don’t fit well with this approach.
If all your functionality is combined in a single Genero application that runs all day then this program will slowly accumulate memory as it executes functionality A, then functionality B, and then functionality C. It is better to break this one monolithic program into several smaller programs dedicated to functionality A, functionality B, functionality C etc and have a small program whose job it is to allow the user to select which of these smaller programs they want to run and to run it. When the user has finished with the small program dedicated to a particular functionality, when they close this program this will free up its memory. With a single monolithic program this memory would be held all day until the user exits the monolithic program.
If you have a program that runs all day, it will never exit and free its memory. With Web Services you can use the MAX_REQUESTS_PER_DVM element to tell a Web Service program to exit every X request so as to free memory.
With a Genero application I have seen POS (Point of Sale) programs that have been setup to run all day and thus hold onto the memory all day. However most of the time the program was sitting idle at a dialog waiting for the staff member to goto the POS terminal and process a new sale. Whilst it was waiting it was holding onto the allocated memory and database cursors. What we did was split the program in two, there was a small program that sat and waited for the staff member to return to the terminal and start to process a new sale. Then there was the program to process the new sale. When the user had finished entering a sale, this program exited, thus freeing up the memory it had been allocated.
Summary
Chances are you are doing a lot of these things already. Generally your coding style, coding habits, and program design goes a long way to the memory consumption, and there isn’t anything explicit you need to do. If you do pay attention, then the benefit will typically be seen in a multiple server / load balanced scenario. Instead of 6 servers to manage your load, you may need only 5 servers.