home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 8
/
FreshFishVol8-CD2.bin
/
bbs
/
dev
/
oberon-a-1.4ß.lha
/
Oberon-A
/
texts
/
Memory.txt
< prev
next >
Wrap
Text File
|
1994-05-28
|
13KB
|
274 lines
$RCSfile: Memory.txt $
Description: Description of the Oberon-A memory management system
Created by: fjc (Frank Copeland)
$Revision: 1.2 $
$Author: fjc $
$Date: 1994/05/13 19:28:33 $
Copyright © 1994, Frank Copeland.
This file is part of Oberon-A.
See Oberon-A.doc for conditions of use and distribution.
________________________________________________________________________
Introduction
------------
This document describes the design and implementation of Oberon-A's
memory management. This covers the allocator (NEW and SYSTEM.NEW),
deallocator (SYSTEM.DISPOSE) and garbage collector (SYSTEM.GC). The
design used for Oberon-A is based closely on the designs discussed in
the Oberon Technical Notes 2 and 5, (see TechNotes.doc).
Memory blocks
-------------
Memory allocation in Oberon must take into account a number of factors
that require more than just requesting a block of memory from the
operating system. Oberon's run-time type checking requires that every
record variable that is dynamically allocated have an hidden type tag
preceding it. The garbage collector must be able to distinguish between
memory allocated for records, arrays and untyped blocks (created by
SYSTEM.NEW). CPointers and BPointers must be treated differently to
normal Oberon pointers.
Three types of memory block are recognised:
RecordBlk : memory allocated to a POINTER TO <record type> variable.
ArrayBlk : memory allocated to a POINTER TO <array type> variable.
SysBlk : memory allocated to a CPointer or BPointer variable, or
allocated with SYSTEM.NEW.
A RecordBlk looks like this, where v is a variable of type POINTER TO T,
T is a record type:
RecordBlk = RECORD
link : ADDRESS; (* next element in list of blocks *)
tag : ADDRESS; (* type descriptor of type T *)
v --> data : ARRAY SIZE(T) OF BYTE
END
An ArrayBlk looks like this, where v is a variable of type POINTER TO
ARRAY n1, n2 ... ni-1 OF T:
ArrayBlk = RECORD
arrpos : LONGINT; (* used by the garbage collector *)
elemSize : LONGINT; (* SIZE (T), for the convenience of the
garbage collector *)
size : LONGINT; (* n1 * n2 ... * ni-1 * SIZE(T) *)
link : ADDRESS; (* next element in list of blocks *)
tag : ADDRESS; (* type descriptor of type T *)
v --> data : ARRAY size OF BYTE
END
A SysBlk looks like this, where v is any pointer variable:
SysBlk = RECORD
link : ADDRESS; (* next element in list of blocks *)
size : LONGINT; (* size of block *)
v --> data : ARRAY size OF BYTE
END
The type of the block is determined by bits encoded in the tag or size
field at address: v - 4. Bits 0 and 1 are used for this encoding: they
are available to be used because the type descriptors are guaranteed to
be longword-aligned and the allocator ensures that block sizes are
multiples of 4 bytes. Bit 0 is set to indicate that the block is a
SysBlk. Bit 1 is set to indicate that the block is an ArrayBlk. If bit
1 is clear, the block is a RecordBlk. This encoding ensures that the
type tag of a RecordBlk is a valid pointer to a type descriptor, which
is necessary for fast run-time type checks and guards. For the other
block types, the encoded bits must be masked out before the tag or size
field can be used.
The link field (at address: v - 8) in each type of block is used to
implement a singly-linked list of allocated blocks. It is either NIL,
or points to the link field in the next block in the list. Blocks are
added to the list by the allocator and removed by the deallocator and
garbage collector. All allocated blocks are freed when the program
terminates. Two seperate lists are maintained: one of traced blocks
known to the garbage collector and one of untraced blocks.
The size, elemSize and arrpos fields in an ArrayBlk block are required
by the garbage collector (see Garbage collection).
Allocating memory
-----------------
The allocator is implemented in two procedures in the run-time support
library: OberonSys_NEW and OberonSys_SYSNEW. OberonSys_NEW is used to
allocate RecordBlk and ArrayBlk blocks and OberonSys_SYSNEW is used to
allocate SysBlk blocks. The standard procedure SYSTEM.NEW always
translates into a call to OberonSys_SYSNEW while the standard procedure
NEW can translate into a call to either, depending on the type of
variable.
Oberon_NEW requires two parameters:
size (passed D0): this is only required for an ArrayBlk and is the
size of the block to be allocated.
tag (passed in D1): a pointer to a type descriptor. Bit 1 is set if an
ArrayBlk is required.
For a RecordBlk block, OberonSys_NEW calculates the size of the block by
adding 8 to the size of the record found in the type descriptor. For an
ArrayBlk block, it adds 20 to the size parameter. The result is rounded
up to the next multiple of 4 bytes. It then asks Exec for a block of
memory of the required size, using AllocMem (size, {memClear}) so that
the block is zeroed. If successful, it initialises the block's hidden
fields, links it into the relevant list and returns the address of the
data field in D0. By default, the allocated block is placed in the
list of blocks known to the garbage collector. If the allocation fails,
NIL is returned in D0.
OberonSys_SYSNEW requires two parameters:
size (passed in D0): the number of bytes to be allocated.
traced (passed in D1.B): a flag indicating if the variable is traced.
OberonSys_SYSNEW adds 8 to the size parameter and rounds the result up
to the next multiple of 4 bytes. It then asks Exec for a block of
memory of the required size, using AllocMem (size, {memClear}) so that
the block is zeroed. If successful, it initialises the block's hidden
fields, links it into the relevant list and returns the address of the
data field in D0. If traced is TRUE, the allocated block is placed in
the list of blocks known to the garbage collector, otherwise it is
placed in an alternate list and will not be garbage collected. If the
allocation fails, NIL is returned in D0.
Type tags and descriptors
-------------------------
RecordBlk and ArrayBlk blocks both contain a type tag, which is a
pointer to a type descriptor. A type descriptor is always associated
with a record type. Information in the type descriptor is used by the
allocator and the garbage collector. The structure of a type descriptor
is:
TypeTag = POINTER TO TypeDesc;
TypeDesc = RECORD
size : LONGINT;
ttable : ARRAY 8 OF TypeTag;
offsets : ARRAY N+1 OF LONGINT
END
size holds the size in bytes of an object of that type. This is used
by the allocator to determine the size of a RecordBlk. ttable holds a
table of pointers to the descriptors of the base types of the
descriptor's type. This is used in type tests and guards. The size is
fixed so that the garbage collector can always locate the offsets field
at a fixed offset from the base of the type descriptor. This limits the
depth to which types can be extended. The depth chosen (8) is the same
as that used in the Oberon System and should be adequate for most
purposes. The offsets field is an array holding the offsets of the
pointer fields in the type; N is the number of such fields. This is
used in the garbage collector's mark phase (see Garbage collection).
Deallocating memory
-------------------
Memory is explicitly deallocated with the standard procedure
SYSTEM.DISPOSE. This is implemented in the run-time support procedure
OberonSys_DISPOSE.
OberonSys_DISPOSE has one parameter:
block (passed in D0): the address of the block to be freed.
OberonSys_DISPOSE scans the lists of allocated blocks looking for block;
the untraced list is scanned first. If it does not find block, it halts
the program. Otherwise, block is removed from the list and the memory
is returned to the operating sys