/*
 * Copyright (c) 2001, 2008, 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 sun.jvm.hotspot.oops;

import java.io.*;
import java.util.*;

import sun.jvm.hotspot.debugger.*;
import sun.jvm.hotspot.runtime.*;
import sun.jvm.hotspot.types.*;
import sun.jvm.hotspot.utilities.*;

Mark is the analogue of the VM's markOop. In this system it does not subclass Oop but VMObject. For a mark on the stack, the mark's address will be an Address; for a mark in the header of an object, it will be an OopHandle. It is assumed in a couple of places in this code that the mark is the first word in an object.
/** Mark is the analogue of the VM's markOop. In this system it does not subclass Oop but VMObject. For a mark on the stack, the mark's address will be an Address; for a mark in the header of an object, it will be an OopHandle. It is assumed in a couple of places in this code that the mark is the first word in an object. */
public class Mark extends VMObject { static { VM.registerVMInitializedObserver(new Observer() { public void update(Observable o, Object data) { initialize(VM.getVM().getTypeDataBase()); } }); } private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { Type type = db.lookupType("oopDesc"); markField = type.getCIntegerField("_mark"); ageBits = db.lookupLongConstant("markOopDesc::age_bits").longValue(); lockBits = db.lookupLongConstant("markOopDesc::lock_bits").longValue(); biasedLockBits = db.lookupLongConstant("markOopDesc::biased_lock_bits").longValue(); maxHashBits = db.lookupLongConstant("markOopDesc::max_hash_bits").longValue(); hashBits = db.lookupLongConstant("markOopDesc::hash_bits").longValue(); lockShift = db.lookupLongConstant("markOopDesc::lock_shift").longValue(); biasedLockShift = db.lookupLongConstant("markOopDesc::biased_lock_shift").longValue(); ageShift = db.lookupLongConstant("markOopDesc::age_shift").longValue(); hashShift = db.lookupLongConstant("markOopDesc::hash_shift").longValue(); lockMask = db.lookupLongConstant("markOopDesc::lock_mask").longValue(); lockMaskInPlace = db.lookupLongConstant("markOopDesc::lock_mask_in_place").longValue(); biasedLockMask = db.lookupLongConstant("markOopDesc::biased_lock_mask").longValue(); biasedLockMaskInPlace = db.lookupLongConstant("markOopDesc::biased_lock_mask_in_place").longValue(); biasedLockBitInPlace = db.lookupLongConstant("markOopDesc::biased_lock_bit_in_place").longValue(); ageMask = db.lookupLongConstant("markOopDesc::age_mask").longValue(); ageMaskInPlace = db.lookupLongConstant("markOopDesc::age_mask_in_place").longValue(); hashMask = db.lookupLongConstant("markOopDesc::hash_mask").longValue(); hashMaskInPlace = db.lookupLongConstant("markOopDesc::hash_mask_in_place").longValue(); biasedLockAlignment = db.lookupLongConstant("markOopDesc::biased_lock_alignment").longValue(); lockedValue = db.lookupLongConstant("markOopDesc::locked_value").longValue(); unlockedValue = db.lookupLongConstant("markOopDesc::unlocked_value").longValue(); monitorValue = db.lookupLongConstant("markOopDesc::monitor_value").longValue(); markedValue = db.lookupLongConstant("markOopDesc::marked_value").longValue(); biasedLockPattern = db.lookupLongConstant("markOopDesc::biased_lock_pattern").longValue(); noHash = db.lookupLongConstant("markOopDesc::no_hash").longValue(); noHashInPlace = db.lookupLongConstant("markOopDesc::no_hash_in_place").longValue(); noLockInPlace = db.lookupLongConstant("markOopDesc::no_lock_in_place").longValue(); maxAge = db.lookupLongConstant("markOopDesc::max_age").longValue(); /* Constants in markOop used by CMS. */ cmsShift = db.lookupLongConstant("markOopDesc::cms_shift").longValue(); cmsMask = db.lookupLongConstant("markOopDesc::cms_mask").longValue(); sizeShift = db.lookupLongConstant("markOopDesc::size_shift").longValue(); } // Field accessors private static CIntegerField markField; // Constants -- read from VM private static long ageBits; private static long lockBits; private static long biasedLockBits; private static long maxHashBits; private static long hashBits; private static long lockShift; private static long biasedLockShift; private static long ageShift; private static long hashShift; private static long lockMask; private static long lockMaskInPlace; private static long biasedLockMask; private static long biasedLockMaskInPlace; private static long biasedLockBitInPlace; private static long ageMask; private static long ageMaskInPlace; private static long hashMask; private static long hashMaskInPlace; private static long biasedLockAlignment; private static long lockedValue; private static long unlockedValue; private static long monitorValue; private static long markedValue; private static long biasedLockPattern; private static long noHash; private static long noHashInPlace; private static long noLockInPlace; private static long maxAge; /* Constants in markOop used by CMS. */ private static long cmsShift; private static long cmsMask; private static long sizeShift; public Mark(Address addr) { super(addr); } public long value() { return markField.getValue(addr); } public Address valueAsAddress() { return addr.getAddressAt(markField.getOffset()); } // Biased locking accessors // These must be checked by all code which calls into the // ObjectSynchoronizer and other code. The biasing is not understood // by the lower-level CAS-based locking code, although the runtime // fixes up biased locks to be compatible with it when a bias is // revoked. public boolean hasBiasPattern() { return (Bits.maskBitsLong(value(), biasedLockMaskInPlace) == biasedLockPattern); } public JavaThread biasedLocker() { Threads threads = VM.getVM().getThreads(); Address addr = valueAsAddress().andWithMask(~(biasedLockMaskInPlace & ageMaskInPlace)); return threads.createJavaThreadWrapper(addr); } // Indicates that the mark gas the bias bit set but that it has not // yet been biased toward a particular thread public boolean isBiasedAnonymously() { return hasBiasPattern() && (biasedLocker() == null); } // lock accessors (note that these assume lock_shift == 0) public boolean isLocked() { return (Bits.maskBitsLong(value(), lockMaskInPlace) != unlockedValue); } public boolean isUnlocked() { return (Bits.maskBitsLong(value(), biasedLockMaskInPlace) == unlockedValue); } public boolean isMarked() { return (Bits.maskBitsLong(value(), lockMaskInPlace) == markedValue); } // Special temporary state of the markOop while being inflated. // Code that looks at mark outside a lock need to take this into account. public boolean isBeingInflated() { return (value() == 0); } // Should this header be preserved during GC? public boolean mustBePreserved() { return (!isUnlocked() || !hasNoHash()); } // WARNING: The following routines are used EXCLUSIVELY by // synchronization functions. They are not really gc safe. // They must get updated if markOop layout get changed. // FIXME // markOop set_unlocked() const { // return markOop(value() | unlocked_value); // } public boolean hasLocker() { return ((value() & lockMaskInPlace) == lockedValue); } public BasicLock locker() { if (Assert.ASSERTS_ENABLED) { Assert.that(hasLocker(), "check"); } return new BasicLock(valueAsAddress()); } public boolean hasMonitor() { return ((value() & monitorValue) != 0); } public ObjectMonitor monitor() { if (Assert.ASSERTS_ENABLED) { Assert.that(hasMonitor(), "check"); } // Use xor instead of &~ to provide one extra tag-bit check. Address monAddr = valueAsAddress().xorWithMask(monitorValue); return new ObjectMonitor(monAddr); } public boolean hasDisplacedMarkHelper() { return ((value() & unlockedValue) == 0); } public Mark displacedMarkHelper() { if (Assert.ASSERTS_ENABLED) { Assert.that(hasDisplacedMarkHelper(), "check"); } Address addr = valueAsAddress().andWithMask(~monitorValue); return new Mark(addr.getAddressAt(0)); } // FIXME // void set_displaced_mark_helper(markOop m) const { // assert(has_displaced_mark_helper(), "check"); // intptr_t ptr = (value() & ~monitor_value); // *(markOop*)ptr = m; // } // markOop copy_set_hash(intptr_t hash) const { // intptr_t tmp = value() & (~hash_mask_in_place); // tmp |= ((hash & hash_mask) << hash_shift); // return (markOop)tmp; // } // it is only used to be stored into BasicLock as the // indicator that the lock is using heavyweight monitor // static markOop unused_mark() { // return (markOop) marked_value; // } // // the following two functions create the markOop to be // // stored into object header, it encodes monitor info // static markOop encode(BasicLock* lock) { // return (markOop) lock; // } // static markOop encode(ObjectMonitor* monitor) { // intptr_t tmp = (intptr_t) monitor; // return (markOop) (tmp | monitor_value); // } // used for alignment-based marking to reuse the busy state to encode pointers // (see markOop_alignment.hpp) // markOop clear_lock_bits() { return markOop(value() & ~lock_mask_in_place); } // // // age operations // markOop set_marked() { return markOop((value() & ~lock_mask_in_place) | marked_value); } // public int age() { return (int) Bits.maskBitsLong(value() >> ageShift, ageMask); } // markOop set_age(int v) const { // assert((v & ~age_mask) == 0, "shouldn't overflow age field"); // return markOop((value() & ~age_mask_in_place) | (((intptr_t)v & age_mask) << age_shift)); // } // markOop incr_age() const { return age() == max_age ? markOop(this) : set_age(age() + 1); } // hash operations public long hash() { return Bits.maskBitsLong(value() >> hashShift, hashMask); } public boolean hasNoHash() { return hash() == noHash; } // FIXME // Prototype mark for initialization // static markOop prototype() { // return markOop( no_hash_in_place | no_lock_in_place ); // } // Debugging public void printOn(PrintStream tty) { if (isLocked()) { tty.print("locked(0x" + Long.toHexString(value()) + ")->"); displacedMarkHelper().printOn(tty); } else { if (Assert.ASSERTS_ENABLED) { Assert.that(isUnlocked(), "just checking"); } tty.print("mark("); tty.print("hash " + Long.toHexString(hash()) + ","); tty.print("age " + age() + ")"); } } // FIXME // // Prepare address of oop for placement into mark // inline static markOop encode_pointer_as_mark(void* p) { return markOop(p)->set_marked(); } // // // Recover address of oop from encoded form used in mark // inline void* decode_pointer() { return clear_lock_bits(); } // Copy markOop methods for CMS here. public boolean isCmsFreeChunk() { return isUnlocked() && (Bits.maskBitsLong(value() >> cmsShift, cmsMask) & 0x1L) == 0x1L; } public long getSize() { return (long)(value() >> sizeShift); } }