function gcmalloc( s ) -- garbage-collection version of malloc; calls malloc and then -- initiates collection of malloc indicates the heap is full. temp = malloc(s) if temp = null return temp else markroots sweep return malloc(s) -- if this returns null, we're really out of memory endif procedure markroots for p = current_ar to global_ar do -- for every word on the stack b = block(p) if b non null -- if the word could be a pointer to a block on the heap mark(b) -- mark that block endif endfor procedure mark(b) if b->gc not marked -- mark the block b found on the heap b->gc = marked for p = b to b + sizeof(b) do -- for every word in the block bb = block(p) if bb non null -- if the word could be a pointer to a block on the heap mark(bb) -- mark that block (recursively) endif endfor endif procedure sweep b = first_block loop nb = next_block( b ) if b->gc is not marked free( b ) else b->gc = not marked endif b = nb until b = last_block
Part B: Here is a correct but very slow implementation of the block(p) function:
function block(p) if p >= first_block and p <= last_block then -- p points somewhere inside the heap b = first_block while b > p or next_block(b) <= p b = next_block(b) end loop -- guaranteed to terminate because p is in heap return b else -- p points outside the heap return null endifFaster implementations of block(p) will be be approximate! Consider:
function block(p) if p >= first_block and p <= last_block then b = p loop if b -> gc marked or b -> gc not marked then -- passed test 1 -- gc field of block is valid if b = prev_block( next_block(p) ) -- passed test 2 -- pointer fields make sense return b endif endif b = b - 1; endloop else -- p points outside the heap return null endifThe above code searches backward from the given pointer until it finds a data structure that resembles a boundary tag. The first test for resemblance is simple -- does the candidate for a tag contain the expected constants (either marked or not marked) in the garbage collection field. If this field is a word, and if the values selected to represent marked and not marked are distinctive, this test will rarely pass except when a real boundary tag is found. The second test checks the integrety of the data structure of the boundary tags, checking to see if the pointers to the next and previous blocks make sense. It is still possible that this test will be passed for a data structure that is not really a boundary tag, but it is extremely unlikely. Additional tests could be added, for example, if every boundary tag includes a checksum on the pointer fields of the tag, this could be tested.
Yet another solution to this problem can be constructed, based on a binary search of the list of all blocks in the heap. This would require that an auxiliary index data structure be maintained to support this search, but the cost of maintaining this auxiliary index is almost certainly less than the cost of the linear search used in the first alternative used above, and the resulting solution would be formally correct.
Problem 4 on page 394 of Tannenbaum asks for the same figure for a 256 processor hypercube. This hypercube is 8 dimensional (an n dimensional hypercube has 2n vertices), and the maximum hop-count occurs between processors on opposite corners of the hypercube. Opposite, in n dimensions, is defined as having coordinates that differ in each of the n dimensions, and the hop-count therefore involves one hop along each dimension. So, for a 256 processor hypercube, the maximum hop-count is 8.