MEMORY
Calculate the Stack space
Local variables, including arrays, are allocated on the stack, which is generally _much_ smaller than the heap. A very large local array will cause the stack to collide with the heap, with a crash.
How much stack space can you safely use? The space available turns out to depend on the compiled application's Get Info memory allocation.
_max=31
LOCAL FN Test&
DIM s$(_max) 'allocate (_max+1)*256 bytes + 12 bytes for a LOCAL FN
END FN=FN STACKSPACE
WINDOW 1
before& = FN STACKSPACE
during& = FN Test&
after& = FN STACKSPACE
PRINT before& "before"
PRINT during& "during"
PRINT after& "after"
PRINT "Used by FN Test&=" before&-during&
DO
UNTIL FN BUTTON
Investigation with a compiled app from the above program, and various memory allocations (in the OS 8.5 Get Info window) shows that FB2 sets the "before" stack as:-
initial_stack_size = min (32440, 1400 + 32*Kpartition)
where Kpartition is the number of K entered.
For example with 8000K the stack is a huge 257400 bytes. With 100K the stack is only 32440 bytes, and a crash should be expected if _max > = 126.
With a global DIM, the memory is taken from the application's heap.
Robert
I ran your program. Thanks.
I changed _max to 5000 and s$ to 100 chars. I ran it uncompiled in FBII with 18000K allocated. When I ran the modified code, during& went negative.
As I decrease the size of _max, during& approaches zero then goes positive.
What does that mean?
_max=5000
LOCAL FN Test&
DIM 100 s$ (_max)
END FN=FN STACKSPACE
WINDOW 1
before& = FN STACKSPACE
during& = FN Test&
after& = FN STACKSPACE
PRINT before& "before"
PRINT during& "during"
PRINT after& "after"
PRINT "Used by FN Test&=" before&-during&
DO
UNTIL FN BUTTON
Pierre A. Zippi
When FN STACKSPACE is negative it means that you
(a) have attempted to use more stack than the system has reserved, and
(b) are a hair's-breadth away from a crash.
The only reason that your program didn't crash was that the array (illegally spilling over the top of the stack) was never accessed in FN Test&. The modified FN Test& below shows how to crash into BowelsOfTheMemoryManager.
Conclusion: don't use the stack for a large array. Either globally DIM it, or use relocatable memory (FN NEWPTR and XREF, or FN NEWHANDLE and XREF@).
_max=5000
LOCAL FN Test&
DIM 100 s$ (_max)
LONG IF FN STACKSPACE<100
INPUT "OK to crash?"; ok$
IF UCASE$(ok$)<>"Y" THEN END
END IF
FOR j=0 TO _max
s$(j)=""
NEXT
END FN=FN STACKSPACE
WINDOW 1
before& = FN STACKSPACE
during& = FN Test&
PRINT before& "before"
PRINT during& "during"
PRINT "Used by FN Test&=" before&-during&
DO: UNTIL FN BUTTON
Robert
>> Conclusion: don't use the stack for a large array. Either globally DIM it, or <<
Now, would someone like to go out on a limb and say _how large_ is large? Is there some absolute number, or a proportion of total application size, or a proportion of the memory you've typically got available or something else entirely? I've only got one situation where I rely on local arrays in a big way, and have never had problems with it. But if there's a danger of collision (stacks? heaps? why can't I use scaffolding and re-bars?) I'd like to be sure I'm staying within the safety zone.
Lucy
(1) This is "Black Art" stuff, but (3) and (4) below help.
(2) Every time a FN is called, its stack allocation is used. The allocation is released at the END FN statement.
(3) The total stack space is not documented in the FB manuals, but by experiment is:-
> initial_stack_size = min (32440, 1400 + 32*Kpartition)
> where Kpartition is the number of K entered
So in the main program of a compiled application you have at least 32K of stack. If the application's Get Info memory is greater than about 1000K, you'll have more.
(4) If stack overflow is a real worry, you can do a check within a FN, before allowing it to call another FN (or itself, as in this example)
LOCAL FN Recursion(depth)
_myArrayBytes=1024 ' for DIM localStuff%(511)
_myOtherVarBytes=100 ' guess (over-generous in this case)
_safetyBytes@00 ' guess
_stackBytesNeededForFN=_myArrayBytes_myOtherVarBytes_safetyBytes
DIM localStuff%(511) 'use 1024 bytes of stack
PRINT "Depth=" depth ; " Stack available=" FN STACKSPACE
LONG IF FN STACKSPACE>_stackBytesNeededForFN 'ok for recursive call?
FN Recursion(depth+1)
XELSE
PRINT "Maximum depth =" depth
END IF
END FN
WINDOW 1
FN Recursion(0)
DO: UNTIL FN BUTTON
As an illustration of Black Art aspect, in the FB environment I get a crash if _safetyBytes is less than about 4000, but in a compiled app _safetyBytes=1000 works OK.
(5) None of this messy stuff is necessary, unless you're allocating substantial chunks of LOCAL variable/array memory, or you're using deep recursion.
Robert
|