/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.vm.memmgr.def; import org.jnode.vm.VmMagic; import org.jnode.annotation.Inline; import org.jnode.annotation.MagicPermission; import org.jnode.vm.classmgr.ObjectFlags; import org.jnode.vm.classmgr.ObjectLayout; import org.jnode.vm.classmgr.VmClassType; import org.jnode.vm.classmgr.VmNormalClass; import org.jnode.vm.classmgr.VmType; import org.jnode.vm.facade.ObjectVisitor; import org.jnode.vm.memmgr.HeapHelper; import org.vmmagic.pragma.UninterruptiblePragma; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.Extent; import org.vmmagic.unboxed.ObjectReference; import org.vmmagic.unboxed.Offset; import org.vmmagic.unboxed.Word; /** * @author epr */ @MagicPermission final class VmDefaultHeap extends VmAbstractHeap implements ObjectFlags { /** * Offset within this heap of the next free memory block */ private Address nextFreePtr; /** * The allocation bitmap as object, so we won't throw it away in a GC cycle */ private Object allocationBitmap; /** * The total size of free space */ private Extent freeSize; /** * Offset (in bytes) from the start of an object to the size of an object */ private Offset sizeOffset; /** * The next heap (linked list) */ private VmDefaultHeap next; /** * Initialize this instance * * @param heapMgr */ public VmDefaultHeap(DefaultHeapManager heapMgr) { super(heapMgr.getHelper()); } /** * Setup the heap object according to the given start-address of the heap. * * @param start * @param heapClass * @param slotSize * @return the heap */ protected static VmDefaultHeap setupHeap(HeapHelper helper, Address start, VmNormalClass<VmDefaultHeap> heapClass, int slotSize) { final int headerSize = ObjectLayout .objectAlign((ObjectLayout.HEADER_SLOTS + 1) * slotSize); final Offset vmtOffset = Offset.fromIntSignExtend(ObjectLayout.TIB_SLOT * slotSize); final Offset sizeOffset = Offset.fromIntSignExtend(-((ObjectLayout.HEADER_SLOTS + 1) * slotSize)); final Offset flagsOffset = Offset.fromIntSignExtend(ObjectLayout.FLAGS_SLOT * slotSize); // Setup a heap object, so the heap can initialize itself. final Address heapPtr = start.add(headerSize); final Word heapObjSize = Word.fromIntZeroExtend(ObjectLayout.objectAlign(heapClass .getObjectSize())); final Word flags = Word.fromIntZeroExtend(ObjectFlags.GC_DEFAULT_COLOR); heapPtr.store(heapObjSize, sizeOffset); heapPtr.store(flags, flagsOffset); heapPtr.store(ObjectReference.fromObject(heapClass.getTIB()), vmtOffset); helper.clear(heapPtr, heapObjSize.toInt()); VmDefaultHeap heap = (VmDefaultHeap) heapPtr.toObjectReference().toObject(); heap.helper = helper; return heap; } /** * Append a new heap to the end of the linked list of heaps. * * @param newHeap */ protected final void append(VmDefaultHeap newHeap) { VmDefaultHeap heap = this; while (heap.next != null) { heap = heap.next; } heap.next = newHeap; } /** * Gets the next heap in the linked list of heaps. * * @return Next heap */ @Inline public final VmDefaultHeap getNext() { return next; } /** * Initialize this heap * * @param start Start address of this heap * @param end End address of this heap (first address after this heap) * @param slotSize */ protected void initialize(Address start, Address end, int slotSize) { // Set my variables this.start = start; this.end = end; initializeAbstract(slotSize); this.sizeOffset = Offset.fromIntSignExtend(-((ObjectLayout.HEADER_SLOTS + 1) * slotSize)); this.headerSize = ObjectLayout.objectAlign(this.headerSize + slotSize); final int size = getSize(); final Address myAddr = ObjectReference.fromObject(this).toAddress(); final Word mySize = myAddr.loadWord(sizeOffset); Address firstObject; if (inHeap(myAddr)) { firstObject = myAddr.add(mySize).add(headerSize); } else { firstObject = start.add(headerSize); } // Initialize an allocation bitmap final int allocationBits = size / ObjectLayout.OBJECT_ALIGN; final int allocationBitmapSize = ObjectLayout .objectAlign((allocationBits + 7) / 8); this.allocationBitmapPtr = firstObject; final Address bitmapPtr = this.allocationBitmapPtr; // Make the bitmap an object, so it is easy to manipulate. bitmapPtr.store(Word.fromIntZeroExtend(allocationBitmapSize), sizeOffset); bitmapPtr.store(Word.fromIntZeroExtend(GC_DEFAULT_COLOR), flagsOffset); bitmapPtr.store(ObjectReference.fromObject(VmType.getObjectClass().getTIB()), tibOffset); firstObject = firstObject.add(allocationBitmapSize + headerSize); helper.clear(allocationBitmapPtr, allocationBitmapSize); this.allocationBitmap = allocationBitmapPtr.toObjectReference().toObject(); // Mark this heap in the allocation bitmap setAllocationBit(this, true); // Mark the allocation bitmap in the allocation bitmap setAllocationBit(allocationBitmap, true); // Initialize the remaining space as free object. final Word remainingSize = end.toWord().sub(firstObject.toWord()); final Address ptr = firstObject; ptr.store(remainingSize, sizeOffset); ptr.store(ObjectReference.fromObject(FREE), tibOffset); this.nextFreePtr = ptr; this.freeSize = remainingSize.toExtent(); } /** * Allocate a new instance for the given class. Not that this method cannot * be synchronized, since the synchronization is handled in VmHeap. * * @param vmClass * @param alignedSize * @return Object Null if no space is left. */ protected Object alloc(VmClassType<?> vmClass, int alignedSize) { if (nextFreePtr.EQ(Address.zero())) { /* This heap is full */ return null; } final Offset tibOffset = this.tibOffset; final Word headerSize = Word.fromIntZeroExtend(this.headerSize); final Offset flagsOffset = this.flagsOffset; final Offset sizeOffset = this.sizeOffset; Word alignedSizeW = Word.fromIntZeroExtend(alignedSize); final Word totalSize = alignedSizeW.add(headerSize); final Object tib = vmClass.getTIB(); if (tib == null) { throw new IllegalArgumentException("vmClass.TIB is null"); } //final int size = getSize(); Address objectPtr = Address.zero(); lock(); try { // Search for the first free block that is large enough //Screen.debug("a"); while (objectPtr == null) { final Address ptr = nextFreePtr; final Word objSize = ptr.loadWord(sizeOffset); final Object objVmt = ptr.loadObjectReference(tibOffset); final Address nextPtr = ptr.add(objSize.add(headerSize)); if ((objVmt == FREE) && alignedSizeW.LE(objSize)) { objectPtr = ptr; } else { if (!inHeap(nextPtr)) { // No large enough free space has been found // A collect may recover smaller free spaces in this // heap, but we leave that to a GC iteration. nextFreePtr = Address.zero(); //Screen.debug("B"); return null; } else { this.nextFreePtr = nextPtr; } } } //Screen.debug("A"); final Word curFreeSize = objectPtr.loadWord(sizeOffset); if (curFreeSize.GT(totalSize)) { // Block is larger then we need, split it up. final Word newFreeSize = curFreeSize.sub(totalSize); /*if (newFreeSize <= headerSize) { Unsafe.debug("Block splitup failed"); Unsafe.debug("\ncurFreeSize "); Unsafe.debug(curFreeSize); Unsafe.debug("\ntotalSize "); Unsafe.debug(totalSize); Unsafe.debug("\nnewFreeSize "); Unsafe.debug(newFreeSize); Unsafe.debug("\nheaderSize "); Unsafe.debug(headerSize); throw new Error("Block splitup failed"); }*/ final Address newFreePtr = objectPtr.add(totalSize); // Set the header for the remaining free block newFreePtr.store(newFreeSize, sizeOffset); newFreePtr.store(0, flagsOffset); newFreePtr.store(ObjectReference.fromObject(FREE), tibOffset); // Set the next free offset nextFreePtr = newFreePtr; } else { // The block is not large enough to split up, make the // new object the size of the free block. alignedSizeW = curFreeSize; } // Create the object header objectPtr.store(alignedSizeW, sizeOffset); objectPtr.store(0, flagsOffset); objectPtr.store(ObjectReference.fromObject(tib), tibOffset); // Mark the object in the allocation bitmap setAllocationBit(objectPtr, true); // Fix the freeSize freeSize = freeSize.sub(alignedSizeW); } finally { unlock(); } // Clear the contents of the object. helper.clear(objectPtr, alignedSize); return objectPtr.toObjectReference().toObject(); } /** * Mark the given object as free space. * * @param object */ @Inline final void free(Object object) { final Address ptr = ObjectReference.fromObject(object).toAddress(); final Word objSize = ptr.loadWord(sizeOffset); ptr.store(ObjectReference.fromObject(FREE), tibOffset); setAllocationBit(object, false); freeSize = freeSize.add(objSize); } /** * @return The free size * @see VmAbstractHeap#getFreeSize() */ protected Extent getFreeSize() { return freeSize; } /** * Join all adjacent free spaces. * * @throws UninterruptiblePragma */ protected final void defragment() throws UninterruptiblePragma { final Word size = Word.fromIntZeroExtend(getSize()); final Word headerSize = Word.fromIntZeroExtend(this.headerSize); Word offset = headerSize; final Offset sizeOffset = this.sizeOffset; final Offset tibOffset = this.tibOffset; lock(); try { Address firstFreePtr = Address.zero(); while (offset.LT(size)) { final Address ptr = start.add(offset); final Word objSize = ptr.loadWord(sizeOffset); final Word nextOffset = offset.add(objSize).add(headerSize); final Object vmt = ptr.loadObjectReference(tibOffset); if ((firstFreePtr == null) && (vmt == FREE)) { firstFreePtr = ptr; } if ((vmt == FREE) && (nextOffset.LT(size))) { final Object nextVmt; final Address nextObjectPtr = start.add(nextOffset); nextVmt = nextObjectPtr.loadObjectReference(tibOffset); if (nextVmt == FREE) { // Combine two free spaces Word nextObjSize = nextObjectPtr.loadWord(sizeOffset); Word newObjSize = objSize.add(headerSize).add(nextObjSize); ptr.store(newObjSize, sizeOffset); // Do not increment offset here, because there may be // another next free object, which we will combine // in the next loop. } else { offset = nextOffset; } } else { offset = nextOffset; } } // Set the address of the next free block, to the first free block this.nextFreePtr = firstFreePtr; } finally { unlock(); } } /** * Let all objects in this heap make a visit to the given visitor. * * @param visitor * @param locking If true, use lock/unlock while proceeding to the next object. */ protected final void walk(ObjectVisitor visitor, boolean locking, Word flagsMask, Word flagsValue) { // Go through the heap and call visit on each object final Word headerSize = Word.fromIntZeroExtend(this.headerSize); final Offset sizeOffset = this.sizeOffset; final Offset tibOffset = this.tibOffset; final Object FREE = this.FREE; Word offset = headerSize; final Word size = Word.fromIntZeroExtend(getSize()); if (locking) { while (offset.LT(size)) { final Object tib; final Object object; final Word objSize; final Word flags; lock(); try { final Address ptr = start.add(offset); object = ptr.toObjectReference().toObject(); tib = ptr.loadObjectReference(tibOffset); objSize = ptr.loadWord(sizeOffset); flags = (flagsMask.isZero()) ? Word.zero() : VmMagic.getObjectFlags(object).and(flagsMask); } finally { unlock(); } if (tib != FREE) { if (flags.EQ(flagsValue)) { if (!visitor.visit(object)) { // Stop return; } } } offset = offset.add(objSize).add(headerSize); } } else { while (offset.LT(size)) { final Address ptr = start.add(offset); final Object object = ptr.toObjectReference().toObject(); final Object tib = ptr.loadObjectReference(tibOffset); final Word objSize = ptr.loadWord(sizeOffset); final Word flags = flagsMask.isZero() ? Word.zero() : VmMagic.getObjectFlags(object).and(flagsMask); if (tib != FREE) { if (flags.EQ(flagsValue)) { if (!visitor.visit(object)) { // Stop return; } } } offset = offset.add(objSize).add(headerSize); } } } }