Collecting garbage at the wrong time

In this post: Heartbeat: Garbage collection in VFP and .NET are similar, I talked about how the VFP name table is garbage collected.

Here’s a bug that’s been in the product since forever. It involves having a name table overflow and garbage collection occurring at an unexpected time.

Run the code below. It first calls a routine to create many random names to almost fill the name table. Then it tries to execute code in a PRG that contains many PROCs.

If the NUM is large enough, the name table will overflow, causing a name table garbage collection to occur. If the gc occurs while loading the compiled PRG object code, the partially loaded names are marked as garbage and discarded from the name table, causing seemingly random behavior, such as procedure not found or execution of the wrong procedure.

What other random behavior do you get when you run this code?


The correct behavior should be:

1.       if UseNames makes the vars LOCALs, then the gc can successfully free the names and make enough room in the name table, and the test procs run correctly, with VAL and Procno changing correctly. The exact expected behavior can be seen by commenting out both the LOCAL and PUBLIC lines in UseNames

2.       if UseNames makes the vars PUBLIC an error message indicating there are too many names used in the name table.



Here are some reasons why this bug is rarely encountered:


  • It requires an application to use lots of names to fill the name table. Most applications have a fixed number of variables, fields, object, table names and aren’t generating new ones in a loop

  • It requires the garbage collection to occur at a particular place (loading a compiled file which adds enough new names to the name table)






To allow many variables and execution stack, make sure your config.fpw has the lines:







UseNames(1,63000) && Make name table very full

?"Done filling name table"



DO testA



PROCEDURE UseNames(base,n)

      LOCAL i

      FOR i = 1 TO n

            cvar="x"+PADL(base,3,"0")+TRANSFORM(i)    && create a unique var name like "x0011"

            LOCAL (cVar) && create the var as LOCAL

*            PUBLIC (cVar) && create the var as public


RETURN && Local vars get released when they go out of scope


PROCEDURE MakeTest(cTestName,nCnt,nMode)



      ERASE (cTestName+".prg")

      SET TEXTMERGE ON TO (cTestName+".prg") noshow


            \PUBLIC val,Procno



            \Procno = 0

            \DO <<cTestName>>1

            \?"val = ",val

            FOR i = 1 TO nCnt

                  \     PROCEDURE <<cTestName>><<i>>

                  \           Procno=Procno+1

                  \           IF VAL(SUBSTR(PROGRAM(),6)) != Procno

                  \                 ?"***bad***",PROGRAM(),Procno

                  \                 suspend

                  \           ENDIF

                  IF i < 100

                        \           val=val+" "+ program()


                  IF nMode=1

                        \           do <<cTestName>><<i+1>>



            \     PROCEDURE <<cTestName>><<nCnt+1>>

            \           val = val+"Jelly Bean "+TRANSFORM(<<nCnt>>)





      COMPILE (cTestName)





Comments (2)

  1. A customer asked

    1) Is it better to set it to 0 (Auto) or some &quot;larger&quot; number, such as -16 (1024kb).&amp;nbsp;…

  2. Writing programs using .Net is very productive. One reason is because much of memory management is “managed”

Skip to main content