Hmmm, whatever happened to reduce, reuse, recycle?
-----Original Message-----
From: rpg400-l-bounces@xxxxxxxxxxxx [mailto:rpg400-l-bounces@xxxxxxxxxxxx]
On Behalf Of Bryce Martin
Sent: 08 October 2010 19:28
To: RPG programming on the IBM i / System i
Subject: Re: When does manually allocated memory get released?
Hey Mark,
Great explanation. It helps a great deal. I didn't realize the
additional overhead wasn't in the act of deallocating storage, its in the
act of keeping track of the holes. Makes lots of sense. Its amazing how
one can forget how to think this level when you haven't had to do it in 7
years. Just like using pointers... I haven't used one since starting on
this platform. I know I used to in college writing C++ but havn't done it
in the last 4 or more years now. I like having the pointer tool in the
tool box for when it is necessary.
Thanks
Bryce Martin
Programmer/Analyst I
570-546-4777
"Mark S. Waterbury" <mark.s.waterbury@xxxxxxxxxxxxx>
Sent by: rpg400-l-bounces@xxxxxxxxxxxx
10/08/2010 01:04 PM
Please respond to
RPG programming on the IBM i / System i <rpg400-l@xxxxxxxxxxxx>
To
RPG programming on the IBM i / System i <rpg400-l@xxxxxxxxxxxx>
cc
Subject
Re: When does manually allocated memory get released?
Hi, Bryce:
(Why do something in your application code that the system will do for
you automatically?)
When an activation group is reclaimed, the entire heap for that AG is
automatically freed. This will happen whether your program issues:
RCLACTGRP ACTGRP(name) or automatically at "end of job" when all AGs are
reclaimed, including the Default Activation Group (DAG). (The DAG also
has its own associated heap space. )
Suppose you have the sequence:
alloc(A); alloc(B); alloc(C); alloc(D); dealloc(B); alloc(E) ...
Consider what happens. Up to the first 'dealloc' the system just has to
increment a pointer based on the size requested for each allocation.
But, when the first delloc happens (for B in this case), now the heap
has to keep track of these "holes" and their size, and any future
allocations (e..g. alloc(E) in this case) the heap manager must first
search to see if any "holes" are large enough to contain the newly
requested allocation. If the size of "B" was originally greater than or
equal to the size of the newly requested "E", then the same space can be
re-used. If the size of "E" is less than the size of "B", there will
still be a smaller "hole" left over. This is called "fragmentation" and
is a common problem with all heaps. (The equivalent MI instructions are
FREHSS and REALCHSS, or the equivalent ILE CEE APIs: CEEFRST and CEECZST
respectively).
By avoiding the use of "dealloc" (and FREHSS and CEEFRST) you can avoid
all of this "extra overhead" and you will never have to worry about any
heap storage "fragmentation" because the pointer to the free space is
always moving in one direction (further into the free space).
ILE heaps also support the concepts of "mark and release" where you can
do something like this:
alloc(A); mark(M1); alloc(B); alloc(C); mark(M2); alloc(D);
alloc(E); release(M2); alloc(F); alloc(G); release(M1); alloc(G); ...
In this case, so long as your allocations are "linear" (like a stack),
you can then free all space back to a previous "mark" and then continue
from there. This method requires less overhead than "dealloc" or
"realloc", and allows your applications to reuse some of the dynamic
heap storage when no longer needed, while the application remains "up
and running." (You can use CEEMKHP and CEERLHP to mark and release heap
storage, in addition to the MI instructions SETHSSMK and FREHSSMK,
respectively.) But, there is still more overhead as soon as you use
"mark" and "release" than only using "allocate".
You can also create multiple heaps (e.g. separate "pools" of storage),
using the CEECRHP API (or the MI CRTHS instruction), and then allocate
from that heap, using the CEEGTST or the ALCHS MI instruction. Then, you
can delete an entire "pool" of storage, by calling the CEEDSHP API or
issuing the DESHS MI instruction. The idea here is to group similar
allocated objects with similar "life-times" into the same pool, so they
can then all be reclaimed or disposed of at the same time.
The other design alternative for dynamic storage management is to use a
"garbage collected" heap, as in the Java virtual machine. The ILE heaps
do not provide any automatic garbage collection built-in, so you must
manage the storage yourself, as described above.
So, the "best simple" design approach is to use ALLOC (or %alloc) or
CEEGTST or the MI ALCHS instruction) and _never_ explicitly free the
storage; just leave it to "end of job" or end of activation group and
allow the operating system clean it up automatically.
The best way to eliminate such "extra overhead" is -- just don't do it.
I hope that helps ...
Mark S. Waterbury
On 10/8/2010 10:53 AM, Bryce Martin wrote:
Well there definitely seems to be some differing schools of thought on
this one. I'm thinking that a hybrid approach. Maybe not specifcally
doing a %dealloc but registering the CEETREC to do an end actgrp since
it
will be named. That way the activation group always gets destroyed and
the dealloc is always handled by the system. One qustion though.... why
would the %dealloc be more overhead than the system doing the same thing
on its own?
As an Amazon Associate we earn from qualifying purchases.