4 Memory Management

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 62

Memory Management

Memory allocation

• Three mechanisms for memory allocation


◦ Static. Memory allocated at compile time
◦ Dynamic. Memory allocated at execution time
• Stack: Objects allocated LIFO
• Heap: Objects can be allocated and deallocated at any time
Static allocation

• An object has an absolute address that is maintained throughout the


execution of a program
• The following are usually statically allocated
◦ Global variables
◦ Local variables of subprograms, in the absence of recursion
◦ Constants determined at run time
◦ Tables used during run time (for type checking, garbage collection
etc.)
Static allocation for subprograms
Static allocation does not permit recursion

• We illustrate the problem with an illegal program (FORTRAN doesn’t


allow recursion)

SUBROUTINE ERROR(N)
IF (N.LE.1) RETURN
CALL ERROR(n-1)
END

CALL ERROR (3)

• Assume this is legal


• There is then a unique static area of memory for storing N
◦ First iteration: Store 3
◦ Second iteration: Store 2
◦ The original value is now lost. . .
Dynamic allocation: Stack

• For every runtime instance of a subprogram, we have an activation


record (or frame), that contains the information about this instance
• In a similar (but simpler) way, each block also has an activation record
• The stack (LIFO) is the natural data structure for this
• While not neccessary, a stack can also be used in languages without
recursion, to reduce memory usage
Activation record for anonymous blocks

• This record consists of three parts


◦ Pointers for dynamic chain
◦ Local variables
◦ Intermediate results
Dynamic allocation with a stack

• Implementation with a stack uses


◦ Sequence of calls
◦ Prologue
◦ Epilogue
◦ Return sequence
• The address of the activation record is not known at compile time
• The pointer of the activation record points to the activation record of
the active block
• The information in the activation record is accessible via its offset
◦ The address is the activation record address plus the offset
◦ The offset is determined statically
◦ The sum is computed with a single machine instruction (load or
store)
Activation record for in-line blocks

• Dynamic link (or control link): Pointer to previous record on the stack
• On entry to block: Push
◦ Dynamic link to new record created
◦ Activation record set to pointer to new record
• On exit from block: Pop
◦ Eliminate pointer to current activation record
◦ Set pointer to the activation record on top of stack
Example

{int x=0;
int y=x+1;
{
int z=(x+y)*(x-y);
};
};

• Start: Push record with space for x and y


• Set values for x and y
• Internal block:
◦ Push record for internal block
◦ Set value for z
◦ Pop record for internal block
• Pop record for external block
• Note that in the internal block, x and y cannot be accessed via the
offset, and we need to follow a chain of pointers
Example
In practice

• In many languages, anonymous blocks can be implemented without a


stack
• The compiler can analyze all the nested blocks
◦ Space can be allocated for all variables
◦ This may waste some memory
◦ But no loss of efficiency due to stack management
Activation records for procedures
Management of the stack
Example

{int fact (int n) {


if (n<=1) return 1;
else return n*fact(n-1);
}}
Example

• Parameters: Corresponding to the value of n in the sequence of calls


• Address for result: Address of the location where the final value of
fact(n) should be placed (in the activation record of the call)
• Intermediate results: Space for storing the value of fact(n-1)
• Local variables (not present in this example)
Example: Recursive call of fact(3)
In more detail

{int fact (int n) {


if (n<=1) return 1;
else return n*fact(n-1);
}}
Dynamic allocation with a heap

• Heap: Region of memory in which blocks (and subblocks) can be


allocated and deallocated at arbitrary moments
• This is necessary when the language allows:
◦ Explicit allocation of memory at runtime
◦ Objects of varying size
◦ Objects whose lifetime is not LIFO
• Heap management is nontrivial
◦ Efficient allocation of space: Avoiding fragmentation
◦ Speed of access
Heap: Blocks of fixed size

• The heap is divided into blocks of fixed size


• At the start: All of these blocks are linked in a free list

Each one has a link to the


Points to the first free next free one
one
Heap: Blocks of fixed size

• Allocation of one or more contiguous blocks


• Deallocation: Restore these blocks to the free list
Heap: Blocks of variable size

• Heap initially contains a single block


• Allocation: Find a free block of the appropriate size
• Deallocation: Restore the block to the free list
Problems

• The operations must be efficient


• Avoid wasting memory
◦ Internal fragmentation
◦ External fragmentation
Internal fragmentation

• The space allocated is of size X


◦ A block of size Y > X is allocated
◦ Space of size Y − X is wasted
External fragmentation

• The needed space is available but not usable, as it is broken up into


pieces that are too small
Management of the free list: Single list

• At the start: A single block, whose size is the size of the heap
• At each allocation request: Find a block of the appropriate size
◦ First fit: First block that is large enough
◦ Best fit: Find the smallest block that is large enough
• If the chosen block is much larger than needed, it is divided into two
pieces, and the unused one is added to the free list
Heap management

• Best fit or first fit?


◦ First fit: Faster, worse use of memory
◦ Best fit: Slower, better use of memory
Heap management

• With a single list: Allocation is linear in the number of free blocks


• Better: Use multiple lists. The division of blocks beween the lists can
be
◦ Static
◦ Dynamic. Two examples:
• Buddy system. k lists, with the kth list having blocks of size
2k
◦ If size 2k requested, but not available, allocate a block of
size 2k+1 divided into 2
◦ If a block of size 2k is deallocated, unite it with its other
half, if this is available
• Fibonacci: Similar, but uses Fibonacci numbers
Implementation of scoping rules

• Static scope
◦ Static chain
◦ Display
• Dynamic scope
◦ A-list
◦ Central table of the environment
Static scoping: How to determine the correct association

{ int x=10;
void foo(){
x++;
}
void fie() {
int x=0;
foo();
}

fie();
}

• foo always accesses the same variable x


• x is stored in a certain access record (here main)
• The access record for foo is at the top of the stack
First case

• foo called inside fie


Second case

• foo called by main()

• In both cases
◦ Determine first the activation record where x is located
◦ Access x via the offset from this activation record
Activation record for static scoping
Activation record for static scoping

• Dynamic link (already seen)


• Static link
◦ Pointer to the activation record of the block that immediately
contains the text of the current block
• Note
◦ A dynamic link depends on the execution sequence of the program
◦ A static link depends on the static nesting of the declarations of
the procedure
Static chain: Example

• Sequence of runtime calls: A, B, C, D, E, C


Static chain: Example

• If a subprogram is at level k of nesting, the chain has length k


Example

{ int x;
void A() { x=x+1; }
void B() { int x;
void C() (int y){
int x;
x=y+2;
A();
}
x=0; A(); C(3);
}
x=10;
B();
}
Runtime support

• How is the static link of the call determined?


• The caller should determine the static link
• The caller has the following information
◦ Static nesting of blocks, determined by the compiler
◦ Its own activation record
Runtime support
Determining the pointer

• The caller Ch knows the block nesting


◦ When Ch calls P , it knows that the definition of P is
• Immediately included inCh (k = 0)
• In a block k steps from Ch
• No other case is possible
Determining the pointer

• In this example
◦ Calls A, B, C, D, E, C
◦ Static chain A; (B, 0); (C, 1); (D, 0); (E, 1); (C, 2)
• If k = 0, Ch passes its own SP to P
• If k > 0, we find the pointer after k steps in the static chain
Reducing the costs: The display

• We can reduce the costs from scanning the chain to a constant using
the display
• The static chain is represented by an array, called the display
◦ The ith element of the display is a pointer to the activation record
of the subprogram at nesting level i
◦ If the subprogram is at nested level i, then an object in the external
scope at level h, can be found in the display at level j = i − h
Display

• In the following diagram, Display[i] is the pointer to the activation


record of level i
• The sequence of calls is A, B, C, D, E, C
Display
Display

• Display[i] is the pointer to the activation record of the procedure at


level i
• In this example, the sequence of calls is A, B, C, D, E, C
Display

• If the current procedure is at nesting level i, then the nesting scope at


level h is in Display[i − h]
• If Display is in main memory, an object can be found with two memory
accesses
Management of the Display

• Procedure call manages the display


◦ When Ch calls P at nesting level j, P saves the value of its own
Display[j] in its own activation record, and sets the pointer to
point to a copy of its new activation record pointer
• Modern implementations rarely use this technique, as static chains
longer than 3 are rare
Dynamic scoping

• Under dynamic scoping, the association between names and denotable


objects depends on
◦ The flow of control at runtime
◦ The order in which subprograms are called
• The basic rule is simple:
◦ The current association for a name is that determined by the last
association to have been called and not yet destroyed
Implementation is simple

• Store the names in the activation record


• Search for names via the stack (note: the names themselves may be
long)
• Example: Calls are A, B, C, D
Implementation

• Nonactive associations are in grey


Alternative: A-list

• The associations are stored in an appropriate structure, managed as a


stack
• The same example
A-list

• Simple to implement
• Memory use: Names are listed explicitly
• Management costs:
◦ Entrance/exit from a block: Insertion/removal from a stack
• Access cost: Linear in the depth of the A-list
• The average access cost can be reduced, but at the cost of increasing
the work on entrance/exit from a block
Central table of references (CRT)

• Avoids the costly scanning of A-lists


• The table stores all the distinct names of the program
◦ Static: Access in constant time
◦ Otherwise, use hash functions
• Each name has an association list
◦ Most recent first
◦ Followed by the inactive ones
• Constant access time
Example

• Sequence of calls: A, B, C, D
Analysis of CRT

• More complex than A-list


• Less memory use
◦ If names are used statically, the names themselves are not needed
◦ In any case, each name is stored only once
• Costs of management
◦ Entry/exits from a block: Management of all of the lists of all the
names present in the block
• Access time: Constant (2 indirect accesses)
Exercises
Exercise 1
Using some pseudo-language, write a fragment of code such that the max-
imum number of activation records present on the stack at runtime is not
statically determinable
Exercise 2
In some pseudo-language, write a recursive function such that the maximum
number of activation records present at runtime on the stack is statically
determinable. Can this example be generalised?
Exercise 3
Consider the following code fragment:

A:{int X= 1;
...
B:{X =3;
....
}
....
}

Assume that B is nested one level deeper that A. To resolve the reference to
X present in B, why is it not enough to consider the activation record which
immediately precedes that of B on the stack? Provide a counter-example
filling the spaces in the fragment with dots with appropriate code
Exercise 4
Consider the following program fragment written in a pseudo-language using
static scoping.

void P1 {
void P2 { body-of-P2
} void P3 {
void P4 { body-of-P4 }
body-of-P3 }
body-of-P1
}

Draw the activation record stack region that occurs between the static and
dynamic chain pointers when the following sequence of calls, P 1, P 2, P 3,
P 4, P 2 has been made (is it understood that at this time they are all active:
none has returned).
Exercise 5

Given the following code fragment in a pseudo-language with static scope


and labelled nested blocks (indicated by A: { ... })

A: { int x = 5; goto C;
B: {int x = 4; goto E;
}
C: {int x = 3;
D: {int x = 2;
}
goto B;
E: {int x = 1; // (**)
}
}
}

The static chain is handled using a display. Draw a diagram showing the
display and the stack when execution reaches the point indicated with the
comment (**). As far as the activation record is concerned, indicate what
is the only piece of information required for display handling
Exercise 6
Is it easier to implement the static scope rule or the one for dynamic scope?
Give your reasons.

You might also like