/* * Copyright (c) 2012, 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.heap.gcx; import static com.sun.max.vm.heap.gcx.HeapFreeChunk.*; import com.sun.max.annotate.*; import com.sun.max.memory.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.actor.holder.*; import com.sun.max.vm.classfile.constant.*; import com.sun.max.vm.heap.*; import com.sun.max.vm.layout.*; import com.sun.max.vm.log.VMLog.Record; import com.sun.max.vm.log.hosted.*; import com.sun.max.vm.reference.*; import com.sun.max.vm.runtime.*; import com.sun.max.vm.type.*; import com.sun.max.vm.value.*; /** * Implementation of a special "<em>dark matter</em>" object type, used to fill free space that cannot be allocated, so that: * <ul> * <li>Memory management related operations that walk over memory regions comprising dark matter pay no extra cost for skipping dark matter. Examples * include sweep operations of mark-sweep collector, and dirty card scanning in a generational GC with a card-table based remembered set. </li> * <li>The inspector can easily and unambiguously distinguish dark matter.</li> * </ul> * To achieve the above, each span of dark memory is formatted as a special type of object that can be neither allocated nor referenced directly * by application code. * Because dark matter can be of arbitrary size, it is formatted as an instance of the special scalar array type represented by the * {@linkplain DarkMatter#DARK_MATTER_ARRAY DARK_MATTER_ARRAY} array class actor. This class: * <ul> * <li>has no symbolic definition;</li> * <li>is not registered in the class registry;</li> * <li>cannot be named in Java; and</li> * <li>cannot be instantiated via reflection.</li> * </ul> * This class is otherwise equivalent in layout to a long array. As any array can have zero length, the minimum size for an instance is * three words. The smallest pieces of heap space, which are two words wide, are formatted specially as instances of the class {@link SmallestDarkMatter}. */ public final class DarkMatter { @INSPECTED public static final String DARK_MATTER_CLASS_NAME = "dark matter []"; @INSPECTED public static final ArrayClassActor<LongValue> DARK_MATTER_ARRAY = new ArrayClassActor<LongValue>(ClassRegistry.LONG, SymbolTable.makeSymbol(DARK_MATTER_CLASS_NAME)); public static final DarkMatterLogger logger = new DarkMatterLogger(); /** * Boot image generation initialization. The logger may not be initialized properly during boot image generation. * Calling this during heap scheme initialization forces its initialization. */ @HOSTED_ONLY public static void initialize() { logger.checkOptions(); } /** * Variable-less class used to format the smallest possible dark-matter (i.e., two-words space). */ public static class SmallestDarkMatter { @FOLD static DynamicHub hub() { return ClassActor.fromJava(SmallestDarkMatter.class).dynamicHub(); } @FOLD static Word hubOrigin() { return Reference.fromJava(hub()).toOrigin(); } static void format(Address darkMatter) { final Pointer origin = Layout.cellToOrigin(darkMatter.asPointer()); Layout.writeHubReference(origin, Reference.fromJava(hub())); Layout.writeMisc(origin, Word.zero()); // FIXME: Tracing here may lead to issue with GC if used when retiring TLABs during mutator allocation. if (logger.enabled()) { logger.logFormatSmall(darkMatter); } } private SmallestDarkMatter() { } } private DarkMatter() { } private static void reportDarkMatterRef(Pointer pointer, int wordIndex, Pointer origin) { Log.print("Found reference to DarkMatter "); Log.print(origin); Log.print(" @ "); Log.print(pointer); Log.print(" ["); Log.print(wordIndex); if (Heap.bootHeapRegion.isMutableReference(pointer.plus(wordIndex))) { Log.print(" -- mutable boot region reference / refmap index #"); Log.print(Heap.bootHeapRegion.referenceMapIndex(pointer.plusWords(wordIndex))); } Log.println("]"); FatalError.unexpected("Dark Matter must not be referenced"); } public static void checkNotDarkMatterRef(Pointer pointer, int wordIndex) { Pointer origin = pointer.getWord(wordIndex).asPointer(); // only check non-null, non-forwarded reference. if (origin.isNotZero() && Layout.readForwardRef(origin).isZero() && isDarkMatterHub(origin.getWord())) { reportDarkMatterRef(pointer, wordIndex, origin); } } public static Pointer scanCellForDarkMatter(Pointer cell) { final Pointer origin = Layout.cellToOrigin(cell); // Update the hub first so that is can be dereferenced to obtain // the reference map needed to find the other references in the object checkNotDarkMatterRef(origin, Layout.hubIndex()); final Hub hub = Layout.getHub(origin); if (hub == heapFreeChunkHub()) { return cell.plus(toHeapFreeChunk(origin).size); } // Update the other references in the object final SpecificLayout specificLayout = hub.specificLayout; if (specificLayout == Layout.tupleLayout()) { //TupleReferenceMap.visitReferences(hub, origin, this); hub.visitMappedReferences(origin, darkMatterRefCheckerClosure); if (hub.isJLRReference) { checkNotDarkMatterRef(origin, SpecialReferenceManager.referentIndex()); } return cell.plus(hub.tupleSize); } final int length = Layout.readArrayLength(origin); if (specificLayout == Layout.hybridLayout()) { hub.visitMappedReferences(origin, darkMatterRefCheckerClosure); //TupleReferenceMap.visitReferences(hub, origin, this); return cell.plus(Layout.hybridLayout().getArraySize(length)); } else if (specificLayout == Layout.referenceArrayLayout()) { int wordIndex = Layout.firstElementIndex(); final int endIndex = wordIndex + length; while (wordIndex < endIndex) { checkNotDarkMatterRef(origin, wordIndex++); } return cell.plus(Layout.referenceArrayLayout().getArraySize(Kind.REFERENCE, length)); } return cell.plus(Layout.size(origin)); } private final static class NoDarkMatterRefClosure extends PointerIndexVisitor { @Override public void visit(Pointer pointer, int wordIndex) { checkNotDarkMatterRef(pointer, wordIndex); } } private static final NoDarkMatterRefClosure darkMatterRefCheckerClosure = new NoDarkMatterRefClosure(); public static void checkNoDarkMatterRef(Address start, Address end) { Pointer cell = start.asPointer(); while (cell.lessThan(end)) { cell = scanCellForDarkMatter(cell); } } @FOLD public static Size minSize() { return SmallestDarkMatter.hub().tupleSize; } @FOLD private static DynamicHub hub() { return DARK_MATTER_ARRAY.dynamicHub(); } @FOLD private static Size darkMatterHeaderSize() { return Layout.longArrayLayout().getArraySize(Kind.LONG, 0); } @FOLD private static Word hubOrigin() { return Reference.fromJava(hub()).toOrigin(); } /** * Tells whether the value of a word equals the address of a DarkMatter hub. * @param hubWord a word value * @return true if the word value is the origin of a dark matter hub. */ public static boolean isDarkMatterHub(Word hubWord) { return hubWord.equals(hubOrigin()) || hubWord.equals(SmallestDarkMatter.hubOrigin()); } @NEVER_INLINE private static void reportInvalidDarkMatterRange(Address start, Size size) { final boolean lockDisabledSafepoints = Log.lock(); Log.printRange(start, start.plus(size), false); Log.print(" ("); Log.print(size); Log.print(")"); Log.unlock(lockDisabledSafepoints); FatalError.unexpected("Not enough space to format Dark Matter"); } private static void plantDarkMatter(Address start, Size size) { final Pointer origin = Layout.cellToOrigin(start.asPointer()); final int length = size.minus(darkMatterHeaderSize()).unsignedShiftedRight(Word.widthValue().log2numberOfBytes).toInt(); Layout.writeHubReference(origin, Reference.fromJava(hub())); Layout.writeMisc(origin, Word.zero()); if (MaxineVM.isDebug()) { origin.writeWord(Layout.arrayLayout().arrayLengthOffset(), Word.zero()); } Layout.writeArrayLength(origin, length); if (MaxineVM.isDebug()) { Memory.setWords(start.plus(darkMatterHeaderSize()).asPointer(), length, Memory.zappedMarker()); } // FIXME: Tracing here may lead to issue with GC if used when retiring TLABs during mutator allocation. if (logger.enabled()) { logger.logFormat(start, start.plus(size)); } } @FOLD private static Size maxDarkMatterSize() { return Size.G.shiftedLeft(Word.widthValue().log2numberOfBytes); } /** * Format a word-aligned heap region as dark matter. A {@linkplain FatalError} is raised if the region is less than two-words wide. * @param start address to the first word of the region * @param size size of the region */ public static void format(Address start, Size size) { if (size.greaterThan(minSize())) { // Can't use DarkMatter array for formatting very large region (length encoded as. Need to slice it into smaller region. while (size.greaterThan(maxDarkMatterSize())) { FatalError.breakpoint(); plantDarkMatter(start, maxDarkMatterSize()); start = start.plus(maxDarkMatterSize()); size = size.minus(maxDarkMatterSize()); } plantDarkMatter(start, size); } else if (size.equals(minSize())) { SmallestDarkMatter.format(start); } else { reportInvalidDarkMatterRange(start, size); } } /** * Format a word-aligned heap region as dark matter. A {@linkplain FatalError} is raised if the region is less than two-words wide. * @param start address to the first word of the region * @param end address to the end of the last word of the region */ public static void format(Address start, Address end) { format(start, end.minus(start).asSize()); } /* * Interface for logging heap resizing decisions made by the GenSSHeapSizingPolicy. * The interface uses long instead of Size to improve human-readability from the inspector's log views. */ @HOSTED_ONLY @VMLoggerInterface(defaultConstructor = true) private interface DarkMatterLoggerInterface { void format( @VMLogParam(name = "start") Address start, @VMLogParam(name = "end") Address end); void formatSmall( @VMLogParam(name = "start") Address start); } static final class DarkMatterLogger extends DarkMatterLoggerAuto { DarkMatterLogger() { super("DarkMatter", "Dark Matter Formation"); } @Override protected void traceFormat(Address start, Address end) { Log.print("dark matter @ "); Log.printRange(start, end, true); } @Override protected void traceFormatSmall(Address start) { Log.print("small dark matter @ "); Log.println(start); } } // START GENERATED CODE private static abstract class DarkMatterLoggerAuto extends com.sun.max.vm.log.VMLogger { public enum Operation { Format, FormatSmall; @SuppressWarnings("hiding") public static final Operation[] VALUES = values(); } private static final int[] REFMAPS = null; protected DarkMatterLoggerAuto(String name, String optionDescription) { super(name, Operation.VALUES.length, optionDescription, REFMAPS); } protected DarkMatterLoggerAuto() { } @Override public String operationName(int opCode) { return Operation.VALUES[opCode].name(); } @INLINE public final void logFormat(Address start, Address end) { log(Operation.Format.ordinal(), start, end); } protected abstract void traceFormat(Address start, Address end); @INLINE public final void logFormatSmall(Address start) { log(Operation.FormatSmall.ordinal(), start); } protected abstract void traceFormatSmall(Address start); @Override protected void trace(Record r) { switch (r.getOperation()) { case 0: { //Format traceFormat(toAddress(r, 1), toAddress(r, 2)); break; } case 1: { //FormatSmall traceFormatSmall(toAddress(r, 1)); break; } } } } // END GENERATED CODE }