Package org.jnode.vm.memmgr.def

Source Code of org.jnode.vm.memmgr.def.DefaultHeapManager

/*
* $Id$
*
* Copyright (C) 2003-2014 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 java.io.PrintWriter;

import org.jnode.annotation.Inline;
import org.jnode.annotation.MagicPermission;
import org.jnode.vm.BaseVmArchitecture;
import org.jnode.vm.MemoryBlockManager;
import org.jnode.vm.VmMagic;
import org.jnode.vm.classmgr.ObjectFlags;
import org.jnode.vm.classmgr.ObjectLayout;
import org.jnode.vm.classmgr.VmClassLoader;
import org.jnode.vm.classmgr.VmClassType;
import org.jnode.vm.classmgr.VmNormalClass;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.facade.GCStatistics;
import org.jnode.vm.facade.HeapStatistics;
import org.jnode.vm.facade.ObjectFilter;
import org.jnode.vm.facade.ObjectVisitor;
import org.jnode.vm.facade.VmProcessor;
import org.jnode.vm.memmgr.HeapHelper;
import org.jnode.vm.memmgr.VmHeapManager;
import org.jnode.vm.scheduler.Monitor;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.Word;

@MagicPermission
public final class DefaultHeapManager extends VmHeapManager {

    /**
     * Default size in bytes of a new heap
     */
    public static final int DEFAULT_HEAP_SIZE = 16 * 1024 * 1024;

    /**
     * When this percentage of the free memory has been allocated, a GC is
     * triggered (0..1.0)
     */
    public static final float GC_TRIGGER_PERCENTAGE = 0.75f;

    /**
     * The boot heap
     */
    private final VmBootHeap bootHeap;

    /**
     * The GC thread
     */
    private GCThread gcThread;

    /**
     * The finalizer thread
     */
    private FinalizerThread finalizerThread;

    /**
     * Monitor to synchronize heap access
     */
    private Monitor heapMonitor;

    /**
     * Are we low on memory
     */
    private boolean lowOnMemory;

    /**
     * Linked list of all heaps.
     */
    private VmDefaultHeap heapList;

    /**
     * The first heap.
     */
    private final VmDefaultHeap firstNormalHeap;

    /**
     * The heap currently used for allocation
     */
    private VmDefaultHeap currentHeap;

    /**
     * The heap used for allocations during a GC
     */
    private VmDefaultHeap gcHeap;

    /**
     * The class of the default heap type. Set by initialize
     */
    private final VmNormalClass<VmDefaultHeap> defaultHeapClass;

    /**
     * The number of allocated bytes since the last GC trigger
     */
    private int allocatedSinceGcTrigger;

    private int triggerSize = Integer.MAX_VALUE;

    private boolean gcActive;

    private GCManager gcManager;

    /**
     * Make this private, so we cannot be instantiated
     */
    @SuppressWarnings("unchecked")
    public DefaultHeapManager(VmClassLoader loader, HeapHelper helper)
        throws ClassNotFoundException {
        super(helper);
        this.bootHeap = new VmBootHeap(helper);
        // this.writeBarrier = new DefaultWriteBarrier(helper);
        setWriteBarrier(null);
        this.firstNormalHeap = new VmDefaultHeap(this);
        this.currentHeap = firstNormalHeap;
        this.heapList = firstNormalHeap;
        this.defaultHeapClass = (VmNormalClass<VmDefaultHeap>) loader.loadClass(
            VmDefaultHeap.class.getName(), true);
    }

    /**
     * Is the given address the address of an allocated object on this heap?
     *
     * @param ptr The address to examine.
     * @return True if the given address if a valid starting address of an
     *         object, false otherwise.
     */
    @Inline
    public final boolean isObject(Address ptr) {
        if (!ptr.toWord().and(Word.fromIntZeroExtend(ObjectLayout.OBJECT_ALIGN - 1)).isZero()) {
            // The object is not at an object aligned boundary
            return false;
        }
        if (bootHeap.isObject(ptr)) {
            return true;
        }
        VmDefaultHeap heap = heapList;
        while (heap != null) {
            if (heap.isObject(ptr)) {
                return true;
            }
            heap = heap.getNext();
        }
        return false;
    }

    /**
     * Is the system low on memory?
     *
     * @return boolean
     */
    public boolean isLowOnMemory() {
        return lowOnMemory;
    }

    /**
     * Start a garbage collection process
     */
    public final void gc() {
        gcThread.trigger(false);
    }

    /**
     * Gets the size of free memory in bytes.
     *
     * @return long
     */
    public long getFreeMemory() {
        Extent size = Extent.zero();
        VmDefaultHeap h = firstNormalHeap;
        while (h != null) {
            size = size.add(h.getFreeSize());
            h = h.getNext();
        }
        // size += (Unsafe.addressToLong(heapEnd) -
        // Unsafe.addressToLong(nextHeapPtr));
        size = size.add(Extent.fromLong(MemoryBlockManager.getFreeMemory()));
        return size.toLong();
    }

    /**
     * Gets the size of all memory in bytes.
     *
     * @return the size of all memory in bytes
     */
    public long getTotalMemory() {
        long size = bootHeap.getSize();
        VmDefaultHeap h = firstNormalHeap;
        while (h != null) {
            size += h.getSize();
            h = h.getNext();
        }
        // size += (Unsafe.addressToLong(heapEnd) -
        // Unsafe.addressToLong(nextHeapPtr));
        size += MemoryBlockManager.getFreeMemory();
        return size;
    }

    /**
     * Gets the first heap. All other heaps can be iterated through the
     * <code>getNext()</code> method.
     *
     * @return the first heap
     */
    public final VmDefaultHeap getHeapList() {
        return heapList;
    }

    // ------------------------------------------
    // Private natives
    // ------------------------------------------

    protected void initialize() {
        // Set the basic fields
        helper.bootArchitecture(false);
        final BaseVmArchitecture arch = getCurrentProcessor().getArchitecture();
        final int slotSize = arch.getReferenceSize();

        // Initialize the boot heap.
        bootHeap.initialize(helper.getBootHeapStart(), helper.getBootHeapEnd(),
            slotSize);

        // Initialize the first normal heap
        final Address ptr = helper.allocateBlock(Extent
            .fromIntZeroExtend(DEFAULT_HEAP_SIZE));
        firstNormalHeap.initialize(ptr, ptr.add(DEFAULT_HEAP_SIZE), slotSize);

        // Initialize the GC heap
        gcHeap = allocHeap(Extent.fromIntZeroExtend(DEFAULT_HEAP_SIZE), false);
        gcHeap.append(firstNormalHeap);

        // Initialize the total heap list.
        heapList = gcHeap;
    }

    public void start() {
        // Create a Heap monitor
        heapMonitor = new Monitor();
        final BaseVmArchitecture arch = getCurrentProcessor().getArchitecture();
        this.gcManager = new GCManager(this, arch);
        this.gcThread = new GCThread(gcManager, heapMonitor);
        this.finalizerThread = new FinalizerThread(this);
        gcThread.start();
        finalizerThread.start();
        // Calculate the trigger size
        triggerSize = (int) Math.min(Integer.MAX_VALUE, getFreeMemory()
            * GC_TRIGGER_PERCENTAGE);
    }

    /**
     * Allocate a new instance for the given class. Not that this method cannot
     * be synchronized, since obtaining a monitor might require creating one,
     * which in turn needs this method.
     *
     * @param vmClass
     * @param size
     * @return Object
     */
    protected Object allocObject(VmClassType<?> vmClass, int size) {
        // Make sure the class is initialized
        vmClass.initialize();

        final int alignedSize = ObjectLayout.objectAlign(size);
        // final Monitor mon = heapMonitor;

        VmDefaultHeap heap = currentHeap;
        Object result = null;
        int oomCount = 0;

        final Monitor m = heapMonitor;
        // final Monitor m = null;
        if (m != null) {
            m.enter();
        }
        try {
            if (gcActive) {
                if ((heapFlags & TRACE_ALLOC) != 0) {
                    debug("Using GC Heap type ");
                    debug(vmClass.getName());
                }
                result = gcHeap.alloc(vmClass, alignedSize);
                if (result == null) {
                    helper.die("Out of GC heap memory.");
                }
            } else {
                while (result == null) {
                    // The current heap is full
                    if (heap == null) {
                        // Unsafe.debug("allocHeap in allocObject(");
                        // Unsafe.debug(alignedSize);
                        // Unsafe.debug(") ");
                        int newHeapSize = DEFAULT_HEAP_SIZE;
                        if (size > newHeapSize) {
                            // this is a BIG object, try to allocate a new
                            // heap
                            // only for it
                            newHeapSize = size;
                        }
                        if ((heap = allocHeap(Extent
                            .fromIntZeroExtend(newHeapSize), true)) == null) {
                            lowOnMemory = true;
                            // It was not possible to allocate another heap.
                            // First try to GC, if we've done that before
                            // in this allocation, then we're in real panic.
                            if (oomCount == 0) {
                                oomCount++;
                                if ((heapFlags & TRACE_OOM) != 0) {
                                    debug("<oom/>");
                                }
                                gcThread.trigger(true);
                                heap = firstNormalHeap;
                                currentHeap = firstNormalHeap;
                            } else {
                                if ((heapFlags & TRACE_OOM) != 0) {
                                    debug("Out of memory in allocObject(");
                                    debug(size);
                                    debug(")");
                                }
                                throw OOME;
                                // Unsafe.die();
                            }
                        } else {
                            // Unsafe.debug("AO.G");
                            // We successfully allocated a new heap, set it
                            // to current, so we'll use it for the following
                            // allocations.
                            currentHeap = heap;
                        }
                    }

                    result = heap.alloc(vmClass, alignedSize);

                    if (result == null) {
                        heap = (VmDefaultHeap) heap.getNext();
                    }
                }
                lowOnMemory = false;

                allocatedSinceGcTrigger += alignedSize;
                if ((allocatedSinceGcTrigger > triggerSize)
                    && (gcThread != null)) {
                    if ((heapFlags & TRACE_TRIGGER) != 0) {
                        debug("<alloc:GC trigger/>");
                    }
                    allocatedSinceGcTrigger = 0;
                    gcThread.trigger(false);
                }
            }
            vmClass.incInstanceCount();
            // Allocated objects are initially black.
            VmMagic.setObjectFlags(result, Word
                .fromIntZeroExtend(ObjectFlags.GC_DEFAULT_COLOR));
        } finally {
            if (m != null) {
                m.exit();
            }
        }

        return result;
    }

    /**
     * Allocate a new heap with a given size. The heap object itself is
     * allocated on the new heap, so this method can be called even if all other
     * heaps are full.
     *
     * @param size
     * @return The heap
     */
    private VmDefaultHeap allocHeap(Extent size, boolean addToHeapList) {
        // Unsafe.debug("allocHeap");
        final Address start = helper.allocateBlock(size);
        // final Address start = MemoryBlockManager.allocateBlock(size);
        if (start == null) {
            return null;
        }
        final Address end = start.add(size);
        final int slotSize = getCurrentProcessor().getArchitecture()
            .getReferenceSize();
        final VmDefaultHeap heap = VmDefaultHeap.setupHeap(helper, start,
            defaultHeapClass, slotSize);
        heap.initialize(start, end, slotSize);

        if (addToHeapList) {
            heapList.append(heap);
        }
        return heap;
    }

    /**
     * Print the statics on this object on out.
     */
    public void dumpStatistics(PrintWriter out) {
        out.println("WriteBarrier: " + getWriteBarrier());
    }

    /**
     * @return Returns the bootHeap.
     */
    final VmBootHeap getBootHeap() {
        return this.bootHeap;
    }

    /**
     * @param gcActive The gcActive to set.
     */
    final void setGcActive(boolean gcActive) {
        this.gcActive = gcActive;
    }

    /**
     * Sets the currentHeap to the first heap.
     */
    final void resetCurrentHeap() {
        this.currentHeap = this.firstNormalHeap;
        // Recalculate the trigger size
        triggerSize = (int) Math.min(Integer.MAX_VALUE, getFreeMemory()
            * GC_TRIGGER_PERCENTAGE);
    }

    /**
     * Sets the currentHeap to the first heap.
     */
    final void triggerFinalization() {
        finalizerThread.trigger(false);
    }

    public GCStatistics getStatistics() {
        return gcManager.getStatistics();
    }

    /**
     * {@inheritDoc}
     */
    public HeapStatistics getHeapStatistics(ObjectFilter objectFilter) {
        final DefHeapStatistics heapStatistics = new DefHeapStatistics();
        heapStatistics.setObjectFilter(objectFilter);
        final HeapStatisticsVisitor heapStatisticsVisitor = new HeapStatisticsVisitor(
            heapStatistics);

        accept(heapStatisticsVisitor, false);

        return heapStatistics;
    }
   
    private void accept(ObjectVisitor visitor, boolean locking) {
        VmDefaultHeap heap = firstNormalHeap;
        final Word zero = Word.zero();

        while (heap != null) {
            heap.walk(visitor, locking, zero, zero);
            heap = heap.getNext();
        }
    }
   
    /**
     * @see org.jnode.vm.memmgr.VmHeapManager#createProcessorHeapData(org.jnode.vm.facade.VmProcessor)
     */
    public Object createProcessorHeapData(VmProcessor cpu) {
        // No need here, so return null.
        return null;
    }

    /**
     * @see org.jnode.vm.memmgr.VmHeapManager#notifyClassResolved(org.jnode.vm.classmgr.VmType)
     */
    public void notifyClassResolved(VmType<?> vmType) {
        // Do nothing
    }

    /**
     * @see org.jnode.vm.memmgr.VmHeapManager#loadClasses(org.jnode.vm.classmgr.VmClassLoader)
     */
    public void loadClasses(VmClassLoader loader) throws ClassNotFoundException {
        loader.loadClass("org.jnode.vm.memmgr.def.VmBootHeap", true);
        loader.loadClass("org.jnode.vm.memmgr.def.VmDefaultHeap", true);
    }
}
TOP

Related Classes of org.jnode.vm.memmgr.def.DefaultHeapManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.