/* * Copyright (c) 1996-1999 Bill Venners. All Rights Reserved. * * This Java source file is part of the Interactive Illustrations Web * Site, which is delivered in the applets directory of the CD-ROM * that accompanies the book "Inside the Java 2 Virtual Machine" by Bill * Venners, published by McGraw-Hill, 1999, ISBN: 0-07-135093-4. This * source file is provided for evaluation purposes only, but you can * redistribute it under certain conditions, described in the full * copyright notice below. * * Full Copyright Notice: * * All the web pages and Java applets delivered in the applets * directory of the CD-ROM, consisting of ".html," ".gif," ".class," * and ".java" files, are copyrighted (c) 1996-1999 by Bill * Venners, and all rights are reserved. This material may be copied * and placed on any commercial or non-commercial web server on any * network (including the internet) provided that the following * guidelines are followed: * * a. All the web pages and Java Applets (".html," ".gif," ".class," * and ".java" files), including the source code, that are delivered * in the applets directory of the CD-ROM that * accompanies the book must be published together on the same web * site. * * b. All the web pages and Java Applets (".html," ".gif," ".class," * and ".java" files) must be published "as is" and may not be altered * in any way. * * c. All use and access to this web site must be free, and no fees * can be charged to view these materials, unless express written * permission is obtained from Bill Venners. * * d. The web pages and Java Applets may not be distributed on any * media, other than a web server on a network, and may not accompany * any book or publication. * * BILL VENNERS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR PARTICULAR PURPOSE, OR NON-INFRINGEMENT. BILL VENNERS * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY A LICENSEE AS A * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. */ /** * This class represents the garbage collected heap that is * being exercised by this simulation. It contains all the * logic for interacting with the heap. * * @author Bill Venners */ public class GCHeap { private int handlePoolSize; private int objectPoolSize; private ObjectHandle[] handlePool; private int[] objectPool; GCHeap(int hndlPoolSize, int objPoolSize) { handlePoolSize = hndlPoolSize; objectPoolSize = objPoolSize; objectPool = new int[objectPoolSize]; handlePool = new ObjectHandle[handlePoolSize]; // Initialize the object pool by putting a header at the zeroeth int location // that indicates that the entire remainder of the object pool array is one // big contiguous available memory block. objectPool[0] = formMemBlockHeader(true, objectPoolSize); for (int i = 0; i < handlePoolSize; ++i) { handlePool[i] = new ObjectHandle(); handlePool[i].free = true; } } // Returns number of handles in handlePool (2 ints each) public int getHandlePoolSize() { return handlePoolSize; } // Returns number of ints in object pool public int getObjectPoolSize() { return objectPoolSize; } public ObjectHandle getObjectHandle(int i) { return handlePool[i - 1]; // Subtract one because zero is used to indicate null, // so allocateHandle() adds 1 to the index. } public int getObjectPool(int i) { return objectPool[i]; } public void setObjectPool(int i, int value) { objectPool[i] = value; } // formMemBlockHeader() makes an int that contains two pieces of information, // the length of the memory block and whether the memory block is free. The int // is the header for the block and is stored immediately before the block in // the objectPool array. These headers form a kind of linked list of memory blocks // because you can always jump to the next header by adding length to the index // of the current blocks header to get index to the next block's header. The length // variable is in units of ints; it tells how many ints long the memory block is, // including the header int. // // The zeroeth bit of a memory block header is used to indicate freeness. If the bit // is one, the memory block is free. Bits 1 through 31 are the memory block's length // in number of ints. private int formMemBlockHeader(boolean free, int length) { int retVal = length << 1; if (free) { retVal |= 1; } return retVal; } // The length of a memory block includes the header int. public int getMemBlockLength(int header) { return header >> 1; } public boolean getMemBlockFree(int header) { boolean retVal = false; if ((header & 0x1) == 0x1) { retVal = true; } return retVal; } // allocateObject() returns an int which is an index into the handlePool array. This is, // in effect, a handle. Because a handle of zero indicates a null handle, the zeroeth int // of the handlePool array is unused. The index returned by this function is a handle to // the object. // // bytesNeeded -- number of bytes needed by the object for which memory is being // allocated. public int allocateObject(int bytesNeeded, FishIcon fish) { // Initialize the return value to zero, which indicates not enough memory was // available for the new object. int retVal = 0; // Because the objectPool is an array of ints, all objects are int aligned. Calculate // the number of ints required by the new object based on the passed number of bytes // required. This is done by adding 3 (sizeof(int) - 1) to the bytesNeeded and // dividing by 4 (sizeof(int)). So one, two, three, and four byte objects will require // one int. Five, six, seven, and eight byte objects will require two ints, etc... int intsNeeded = (bytesNeeded + 3) / 4; int i = 0; while (i < objectPoolSize) { int header = objectPool[i]; boolean free = false; if (getMemBlockFree(header)) { free = true; } int length = getMemBlockLength(header); if (!free) { // This memory block is not free, so continue by looking at the next memory // block. Add length to the current index into the constant pool to get the // index of the next memory block header. i += length; continue; } // We have found a free block. First, concatenate any other free blocks that may // be contiguous to this one. while ((i + length) < objectPoolSize && getMemBlockFree(objectPool[i + length])) { // The next memory block is also free, so concatenate with the previous // one. This is done by extending the size of the previous memory block // to include the next block. length += getMemBlockLength(objectPool[i + length]); objectPool[i] = formMemBlockHeader(true, length); } // Now that we have a free block, check to see if it is big enough to hold // the object for which the memory was requested. if (length - 1 < intsNeeded) { // This memory block is free, but not big enough for the fat object for // which memory has been requested. So continue by looking at the next // memory block. Add length to the current index into the constant pool // to get the index of the next memory block header. i += length; continue; } // The current free block is big enough for our new object. First, check to see // if there is more than enough memory in this block than that actually required // by the new object. int extraMem = length - intsNeeded - 1; if (extraMem > 0) { // The free block has more than enough memory, so we must split it into // two blocks. The first block will be exactly the right size for our // new object. The second will contain whatever is leftover. This splitting // is accomplished by changing the length of the original header to exactly // equal whatever is required by the new object, then adding a second // header for the leftover memory. objectPool[i] = formMemBlockHeader(true, intsNeeded + 1); objectPool[i + intsNeeded + 1] = formMemBlockHeader(true, extraMem); } // At this point objectPool[i] is exactly the right size for the new object. // All that we need to do now is allocate the handle to the memory. Add one // to i before passing to allocHandle, because i currently points to the header. // The object handle should point past the header at the start of the object // itself. retVal = allocateHandle(i + 1, fish); if (retVal > 0) { objectPool[i] = formMemBlockHeader(false, intsNeeded + 1); } break; } return retVal; } // allocateHandle() returns an int which is an index into the handlePool array. This is, // in effect, a handle. Because a handle of zero indicates a null handle, the zeroeth int // of the handlePool array is unused. public int allocateHandle(int objectHandle, FishIcon fishHandle) { int retVal = 0; for (int i = 0; i < handlePoolSize; ++i) { if (handlePool[i].free) { handlePool[i].free = false; handlePool[i].objectPos = objectHandle; handlePool[i].fish = fishHandle; retVal = i + 1; // Add one because zero means failure. break; } } return retVal; } public void freeObject(int handle) { if (!handlePool[handle - 1].free) { handlePool[handle - 1].free = true; handlePool[handle - 1].fish = null; int objectTableIndex = handlePool[handle - 1].objectPos; int header = getObjectPool(objectTableIndex - 1); int length = getMemBlockLength(header); header = formMemBlockHeader(true, length); setObjectPool(objectTableIndex - 1, header); } } // Returns true if an object was slid public boolean slideNextNonContiguousObjectDown() { boolean retVal = false; int i = 0; while (i < objectPoolSize) { int header = objectPool[i]; boolean free = false; if (getMemBlockFree(header)) { free = true; } int length = getMemBlockLength(header); if (!free) { // This memory block is not free, so continue by looking at the next memory // block. Add length to the current index into the constant pool to get the // index of the next memory block header. i += length; continue; } // We have found a free block. First, concatenate any other free blocks that may // be contiguous to this one. while ((i + length) < objectPoolSize && getMemBlockFree(objectPool[i + length])) { // The next memory block is also free, so concatenate with the previous // one. This is done by extending the size of the previous memory block // to include the next block. length += getMemBlockLength(objectPool[i + length]); objectPool[i] = formMemBlockHeader(true, length); } // See if an object exists at the next location, if not break out of the loop // and return false. There are no other objects that can be slid. if (i + length >= objectPoolSize) { break; } int sliderLength = getMemBlockLength(objectPool[i + length]); for (int j = 1; j < sliderLength; ++j) { objectPool[i + j] = objectPool[i + length + j]; } objectPool[i] = formMemBlockHeader(false, sliderLength); objectPool[i + sliderLength] = formMemBlockHeader(true, length); for (int j = 0; j < handlePoolSize; ++j) { if (!handlePool[j].free && handlePool[j].objectPos == i + length + 1) { handlePool[j].objectPos = i + 1; break; } } retVal = true; break; } return retVal; } }