/* * $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.Unsafe; import org.jnode.vm.BaseVmArchitecture; import org.jnode.vm.VmSystem; import org.jnode.annotation.MagicPermission; import org.jnode.vm.facade.GCStatistics; import org.jnode.vm.facade.VmUtils; import org.jnode.vm.memmgr.HeapHelper; import org.jnode.vm.memmgr.VmHeapManager; import org.jnode.vm.objects.VmSystemObject; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.Word; /** * @author epr */ @MagicPermission final class GCManager extends VmSystemObject implements Uninterruptible { /** * The heap manager */ private final DefaultHeapManager heapManager; /** * The mark stack */ private final GCStack markStack; /** * An object visitor used for marking */ private final GCMarkVisitor markVisitor; /** * An object visitor used for sweeping */ private final GCSweepVisitor sweepVisitor; /** * An object visitor used for setting objects to GC colour white */ private final GCSetWhiteVisitor setWhiteVisitor; /** * The object visitor that verifies the correctness of the object tree */ private final GCVerifyVisitor verifyVisitor; /** * My statistics */ private final DefGCStatistics stats; /** * The low level helper */ private final HeapHelper helper; /** * The write barrier */ private final DefaultWriteBarrier writeBarrier; /** * Debug mode? */ private final boolean debug; /** * Create a new instance */ public GCManager(DefaultHeapManager heapManager, BaseVmArchitecture arch) { this.debug = true || VmUtils.getVm().isDebugMode(); this.heapManager = heapManager; this.writeBarrier = (DefaultWriteBarrier) heapManager.getWriteBarrier(); this.helper = heapManager.getHelper(); this.markStack = new GCStack(); this.markVisitor = new GCMarkVisitor(heapManager, arch, markStack); this.setWhiteVisitor = new GCSetWhiteVisitor(heapManager); this.verifyVisitor = new GCVerifyVisitor(heapManager, arch); this.sweepVisitor = new GCSweepVisitor(heapManager); this.stats = new DefGCStatistics(); } /** * Do a garbage collection cycle. */ final void gc() { // Prepare final VmBootHeap bootHeap = heapManager.getBootHeap(); final VmDefaultHeap firstHeap = heapManager.getHeapList(); stats.lastGCTime = System.currentTimeMillis(); final boolean locking = (writeBarrier != null); final boolean verbose = (heapManager.getHeapFlags() & VmHeapManager.TRACE_BASIC) != 0; helper.stopThreadsAtSafePoint(); heapManager.setGcActive(true); try { // Pre-GC verification if (debug) { if (false) { // Turn back to true in case of problems in the GC. if (verbose) { heapManager.debug("<preverify/>"); } verify(bootHeap, firstHeap); } } // Mark //helper.stopThreadsAtSafePoint(); //heapManager.setGcActive(true); try { if (verbose) { heapManager.debug("<mark/>"); } markHeap(bootHeap, firstHeap, locking); } finally { //heapManager.setGcActive(false); //helper.restartThreads(); } // Sweep if (verbose) { heapManager.debug("<sweep/>"); } sweep(firstHeap); // Cleanup if (verbose) { heapManager.debug("<cleanup/>"); } cleanup(bootHeap, firstHeap); // Verification if (debug) { if (false) { // Turn back to true in case of problems in the GC. if (verbose) { heapManager.debug("<verify/>"); } verify(bootHeap, firstHeap); } } } finally { heapManager.setGcActive(false); heapManager.resetCurrentHeap(); helper.restartThreads(); } // Start the finalization process heapManager.triggerFinalization(); } /** * Mark all live objects in the heap. * * @param bootHeap * @param firstHeap */ private final void markHeap(VmBootHeap bootHeap, VmDefaultHeap firstHeap, boolean locking) { if (writeBarrier != null) { writeBarrier.setActive(true); } final long startTime = VmSystem.currentKernelMillis(); stats.lastMarkIterations = 0; long markedObjects = 0; boolean firstIteration = true; boolean wbChanged = false; do { // Do an iteration reset stats.lastMarkIterations++; markStack.reset(); if (writeBarrier != null) { writeBarrier.resetChanged(); } markVisitor.reset(); markVisitor.setRootSet(true); // Mark all roots helper.visitAllRoots(markVisitor, heapManager); // statics.walk(markVisitor, resolver); // helper.visitAllThreads(threadMarkVisitor); // Mark every object in the rootset // bootHeap.walk(markVisitor, locking, 0, 0); if (!firstIteration) { // If there was an overflow in the last iteration, // we must also walk through the other heap to visit // all grey objects, since we must still mark // their children. markVisitor.setRootSet(false); bootHeap.walk(markVisitor, locking, Word.zero(), Word.zero()); VmDefaultHeap heap = firstHeap; while ((heap != null) && (!markStack.isOverflow())) { heap.walk(markVisitor, locking, Word.zero(), Word.zero()); heap = heap.getNext(); } } // Test for an endless loop if ((markVisitor.getMarkedObjects() == 0) && markStack.isOverflow()) { // Oops... an endless loop Unsafe.debug("Endless loop in markHeap.... going to die"); helper.die("GCManager.markHeap"); } // Do some cleanup markedObjects += markVisitor.getMarkedObjects(); firstIteration = false; if (writeBarrier != null) { wbChanged = writeBarrier.isChanged(); } } while (markStack.isOverflow() || wbChanged); final long endTime = VmSystem.currentKernelMillis(); stats.lastMarkDuration = endTime - startTime; stats.lastMarkedObjects = markedObjects; if (writeBarrier != null) { writeBarrier.setActive(false); } } /** * Sweep all heaps for dead objects. * * @param firstHeap */ private void sweep(VmDefaultHeap firstHeap) { final long startTime = VmSystem.currentKernelMillis(); VmDefaultHeap heap = firstHeap; while (heap != null) { heap.lock(); sweepVisitor.setCurrentHeap(heap); heap.walk(sweepVisitor, false, Word.zero(), Word.zero()); heap.unlock(); heap = heap.getNext(); } final long endTime = VmSystem.currentKernelMillis(); stats.lastSweepDuration = endTime - startTime; } /** * Mark all objects white, so a next GC action is valid * * @param bootHeap * @param firstHeap */ private void cleanup(VmBootHeap bootHeap, VmDefaultHeap firstHeap) { final long startTime = VmSystem.currentKernelMillis(); bootHeap.walk(setWhiteVisitor, true, Word.zero(), Word.zero()); VmDefaultHeap heap = firstHeap; while (heap != null) { heap.defragment(); //heap.walk(setWhiteVisitor, locking); heap = heap.getNext(); } final long endTime = VmSystem.currentKernelMillis(); stats.lastCleanupDuration = endTime - startTime; } /** * Verify all heaps. * * @param bootHeap * @param firstHeap */ private void verify(VmBootHeap bootHeap, VmDefaultHeap firstHeap) { final long startTime = VmSystem.currentKernelMillis(); final Word zero = Word.zero(); verifyVisitor.reset(); bootHeap.walk(verifyVisitor, true, zero, zero); VmDefaultHeap heap = firstHeap; while (heap != null) { heap.walk(verifyVisitor, true, zero, zero); heap = heap.getNext(); } final int errorCount = verifyVisitor.getErrorCount(); if (errorCount > 0) { Unsafe.debug(errorCount); Unsafe.debug(" verify errors. "); helper.die("Corrupted heap"); } final long endTime = VmSystem.currentKernelMillis(); stats.lastVerifyDuration = endTime - startTime; } public GCStatistics getStatistics() { return stats; } }