You’ve been working in GLASS for a bit and wham! Every time you fire off this one expression, you run out of memory. Getting a

GemStone: Error         Fatal
VM temporary object memory is full
, old space overflow
Error Category: 231169 [GemStone]
  Number: 4067 Arg
  Count: 1
  Context : 20
  Arg 1:   20

isn’t fun. Before you head to Fry’s to buy more memory for your computer, there are a couple of things that you can do.

Batch Loading

Simple Commit

If you are doing a batch load, you can simply add a commit to your script inside a loop:

...do: [:i |
    System commitTransaction.

You can probably get away with doing a commit every couple of hundred iterations depending upon on how much data is being created.

Auto Commit

If there’s no convenient spot to insert a commit, you can build an AlmostOutOfOfMemory handler that will do a commit when you are in danger of running out of temporary object space. A caveat for using this script is that object structure that you’re building needs to be rooted in a persistent object.

| commitThreshold |
commitThreshold :=  75.
"Install AlmostOutOfMemory handler"
  category: GemStoneError
  number: (ErrorSymbols at: #rtErrSignalAlmostOutOfMemory)
  do: [:ex :cat :num :args |
      "Exception caught, do a commit."
      System commitTransaction 
          ifFalse: [ nil error: 'AutoCommit failed' ].
      "run a markSweep"
      System _vmMarkSweep.
      (System _tempObjSpacePercentUsedLastMark <
          ifTrue: [
            "We dropped below the threshold,
             reenable the signal"
            System enableAlmostOutOfMemoryError ].
      "continue execution" ].
"Enable AlmostOutOfMemory signal"
System signalAlmostOutOfMemoryThreshold: commitThreshold.
[...put your code here...]
    ensure: [
        "disable AlmostOutOfMemory signal"
        System signalAlmostOutOfMemoryThreshold: -1].

Debugging Out of Memory Condition

If you’re not doing a bulk load, then you will need to debug the out of memory condition itself. Just today I was running unit tests and part way through the tests, the system ran out of temporary object space. I solved my problem and figured that it was a good idea to share the technique.

You start by enabling the out of memory signal (without defining a handler).

"enable AlmostOutOfMemory signal at 75% of
 temporary object space"
System signalAlmostOutOfMemoryThreshold: 75.

You can execute this statement in a workspace and it will be valid for your entire session. The debugger will come up when the available temporary object space crosses the 75% threshold allowing you to see what your application is doing when it runs out of memory.

If you need more information to help figure things out, the following script provides you with information about the objects that are filling up your temporary object space. See the method comment in System class>>_vmInstanceCounts: for additional options.

| ar totalSize instances sizes |
instances := IdentityDictionary new.
sizes := IdentityDictionary new.
totalSize := 0.
System _generationScavenge_vmMarkSweep.
ar := System _vmInstanceCounts: 3. "old space"
ar associationsDo: [:assoc |
      at: assoc key name "class"
      put: (assoc value at: 1). "instance count"
 totalSize := totalSize + (assoc value at: 2).
       at: assoc key name "class"
      put: (assoc value at: 2) "total bytes" ].
^{ "label"
       '75% full'.
   "total size of objects in temporary object space"
   "sorted list of classes and their total size in bytes"
       sizes associations sortWithBlock: [:a :b | 
           a value >= b value ].
   "sorted list of clases and their instance counts"
       instances associations sortWithBlock: [:a :b | 
           a value >= b value ]}

If you need more samples, you could wrap this script with an auto commit handler that would allow you to sample the population of temporary object space at various thresholds (say 25%, 50%, and 75%).

[1] Photo by Brent and MariLynn via Flickr (Creative Commons).