/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.vm.runtime;
import static com.sun.max.vm.heap.SpecialReferenceManager.*;
import static com.sun.max.vm.thread.VmThreadLocal.*;
import com.sun.max.annotate.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.heap.*;
import com.sun.max.vm.monitor.*;
import com.sun.max.vm.stack.*;
import com.sun.max.vm.thread.*;
/**
* The procedure that is run by the GC thread to perform a garbage collection.
*/
public abstract class GCOperation extends VmOperation {
/**
* Executes a single garbage collection.
*
* @param invocationCount the number of previous executions
*/
protected abstract void collect(int invocationCount);
@Override
protected boolean disablesHeapAllocation() {
return true;
}
/**
* Stops the current mutator thread for a garbage collection. Just before stopping, the
* thread prepares its own stack reference map up to the trap frame. The remainder of the
* stack reference map is prepared by a GC thread once this thread has stopped by blocking
* on the global GC and thread lock ({@link VmThreadMap#THREAD_LOCK}).
*/
@Override
protected void doAtSafepointBeforeBlocking(Pointer trapFrame) {
// note that this procedure always runs with safepoints disabled
final Pointer tla = SafepointPoll.getLatchRegister();
final Pointer etla = ETLA.load(tla);
Heap.disableAllocationForCurrentThread();
if (!LOWEST_ACTIVE_STACK_SLOT_ADDRESS.load(etla).isZero()) {
FatalError.unexpected("Stack reference map preparer should be cleared before GC");
}
VmThreadLocal.prepareStackReferenceMapFromTrap(tla, trapFrame);
}
@Override
public void doAtSafepointAfterBlocking(Pointer trapFrame) {
final Pointer tla = SafepointPoll.getLatchRegister();
final Pointer etla = ETLA.load(tla);
if (!LOWEST_ACTIVE_STACK_SLOT_ADDRESS.load(etla).isZero()) {
FatalError.unexpected("Stack reference map preparer should be cleared after GC");
}
Heap.enableAllocationForCurrentThread();
}
@Override
public void doAfterFrozen(VmThread vmThread) {
Pointer tla = vmThread.tla();
final boolean threadWasInNative = LOWEST_ACTIVE_STACK_SLOT_ADDRESS.load(tla).isZero();
if (threadWasInNative) {
if (VmOperationThread.TraceVmOperations) {
Log.print("Building full stack reference map for ");
Log.printThread(VmThread.fromTLA(tla), true);
}
// Since this thread is in native code it did not get an opportunity to prepare any of its stack reference map,
// so we will take care of that for it now:
stackReferenceMapPreparationTime += VmThreadLocal.prepareStackReferenceMap(tla);
} else {
// Threads that hit a safepoint in Java code have prepared *most* of their stack reference map themselves.
// The part of the stack between the trap stub frame and the frame of the JNI stub that enters into the
// native code for blocking on VmThreadMap.ACTIVE's monitor is not yet prepared. Do it now:
if (VmOperationThread.TraceVmOperations) {
Log.print("Building partial stack reference map for ");
Log.printThread(VmThread.fromTLA(tla), true);
}
final StackReferenceMapPreparer stackReferenceMapPreparer = vmThread.stackReferenceMapPreparer();
stackReferenceMapPreparer.completeStackReferenceMap(tla);
stackReferenceMapPreparationTime += stackReferenceMapPreparer.preparationTime();
}
}
@Override
protected void doBeforeThawingThread(VmThread thread) {
// Indicates that the stack reference map for the thread is once-again unprepared.
LOWEST_ACTIVE_STACK_SLOT_ADDRESS.store3(thread.tla(), Address.zero());
}
long stackReferenceMapPreparationTime;
public GCOperation(String name) {
super(name == null ? "GC" : name, null, Mode.Safepoint, false);
}
/**
*
*/
@Override
protected boolean doItPrologue(boolean nested) {
if (!nested) {
// The lock for the special reference manager must be held before starting GC
Monitor.enter(REFERENCE_LOCK);
} else {
// The VM operation thread must not attempt to lock REFERENCE_LOCK as doing
// so may deadlock the system.
}
return true;
}
@Override
protected void doItEpilogue(boolean nested) {
if (Heap.logGCTime()) {
Heap.timeLogger.logStackReferenceMapPreparationTime(stackReferenceMapPreparationTime);
stackReferenceMapPreparationTime = 0;
}
if (!nested) {
// Notify the reference handler thread so it can process any pending references.
REFERENCE_LOCK.notifyAll();
Monitor.exit(REFERENCE_LOCK);
} else {
// The VM operation thread cannot notify the REFERENCE_LOCK as it doesn't hold.
// This notification will occur during the next non-nested GC operation.
}
}
@Override
public void doIt() {
// The next 2 statements *must* be adjacent as the reference map for this frame must
// be the same at both calls.
stackReferenceMapPreparationTime = VmThreadLocal.prepareCurrentStackReferenceMap();
collect();
}
private int invocationCount;
public int invocationCount() {
return invocationCount;
}
@NEVER_INLINE
private void collect() {
final long k = Size.K.toLong();
long beforeFree = 0L;
long beforeUsed = 0L;
invocationCount++;
if (Heap.LogGCSuppressionCount > 0) {
Heap.LogGCSuppressionCount--;
}
if (Heap.verbose()) {
Log.print("--Start GC ");
Log.print(invocationCount);
Log.println("--");
beforeUsed = Heap.reportUsedSpace();
beforeFree = Heap.reportFreeSpace();
final boolean lockDisabledSafepoints = Log.lock();
Log.print("--Before GC used: ");
Log.print(beforeUsed / k);
Log.print(" Kb, free: ");
Log.print(beforeFree / k);
Log.println(" Kb --");
Log.unlock(lockDisabledSafepoints);
}
collect(invocationCount);
if (Heap.verbose()) {
final long afterUsed = Heap.reportUsedSpace();
final long afterFree = Heap.reportFreeSpace();
final long reclaimed = beforeUsed - afterUsed;
final boolean lockDisabledSafepoints = Log.lock();
Log.print("--After GC used: ");
Log.print(afterUsed / k);
Log.print(" Kb, free: ");
Log.print(afterFree / k);
Log.print(" Kb, reclaimed: ");
Log.print(reclaimed / k);
Log.println(" Kb --");
Log.print("--End GC ");
Log.print(invocationCount);
Log.println("--");
Log.unlock(lockDisabledSafepoints);
}
}
}