--- old/src/share/classes/sun/misc/Unsafe.java 2013-08-19 14:02:51.000000000 -0700 +++ new/src/share/classes/sun/misc/Unsafe.java 2013-08-19 14:02:51.000000000 -0700 @@ -186,7 +186,7 @@ *

* Unless the reference x being stored is either null * or matches the field type, the results are undefined. - * If the reference o is non-null, car marks or + * If the reference o is non-null, card marks or * other store barriers for that object (if the VM requires them) * are updated. * @see #putInt(Object, int, int) @@ -434,6 +434,68 @@ public native void putDouble(long address, double x); /** + * Possible storage modes for references. + * + * The {@link StorageMode#DEFAULT} storage mode uses the JVM's setting for the respective + * storage class. + */ + public enum StorageMode { + DEFAULT, + UNCOMPRESSED_OOP, + COMPRESSED_OOP, + UNCOMPRESSED_KLASS, + COMPRESSED_KLASS; + + static { + for (StorageMode mode : StorageMode.values()) { + setStorageModeConstant(mode.name(), mode.ordinal()); + } + }; + } + + /** + * Tell the JVM about the storage mode values. + */ + private static native void setStorageModeConstant(String name, int ordinal); + + /** + * Fetches a reference value from a given Java variable using the given storage mode. + * @see #getInt(Object, long) + * @since 1.8 + */ + public native Object getObject(Object o, long offset, StorageMode mode); + + /** + * Stores a reference value into a given Java variable using the given storage mode. + *

+ * Unless the reference x being stored is either null + * or matches the field type, the results are undefined. + * If the reference o is non-null, card marks or + * other store barriers for that object (if the VM requires them) + * are updated. + * @see #putInt(Object, int, int) + * @since 1.8 + */ + public native void putObject(Object o, long offset, Object x, StorageMode mode); + + /** + * Fetches a meta data reference value from a given Java variable using the given storage mode. + * @see #getInt(Object, long) + * @since 1.8 + */ + public native long getMetadata(Object o, long offset, StorageMode mode); + + /** + * Stores a meta data reference value into a given Java variable using the given storage mode. + *

+ * Unless the reference x being stored is either null + * or matches the field type, the results are undefined. + * @see #putInt(Object, int, int) + * @since 1.8 + */ + public native void putMetadata(Object o, long offset, long x, StorageMode mode); + + /** * Fetches a native pointer from a given memory address. If the address is * zero, or does not point into a block obtained from {@link * #allocateMemory}, the results are undefined. --- /dev/null 2013-08-19 14:02:59.000000000 -0700 +++ new/test/sun/misc/Unsafe/UnsafeMemoryAccessWithStorageMode.java 2013-08-19 14:02:57.000000000 -0700 @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2013, 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. + */ + +/* @test + * @bug 8022853 + * @summary test that we can read and write uncompressed and compressed oops and Klass pointers + * + * @compile -XDignore.symbol.file UnsafeMemoryAccessWithStorageMode.java + * + * @run junit/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-UseCompressedOops -XX:-UseCompressedKlassPointers UnsafeMemoryAccessWithStorageMode + * @run junit/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCompressedOops -XX:-UseCompressedKlassPointers UnsafeMemoryAccessWithStorageMode + * @run junit/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-UseCompressedOops -XX:+UseCompressedKlassPointers UnsafeMemoryAccessWithStorageMode + * @run junit/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UseCompressedOops -XX:+UseCompressedKlassPointers UnsafeMemoryAccessWithStorageMode + */ + +import org.junit.*; + +import static org.junit.Assert.*; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.reflect.*; +import java.util.*; + +import sun.misc.Unsafe; +import sun.misc.Unsafe.*; + +import com.sun.management.HotSpotDiagnosticMXBean; + +import sun.management.ManagementFactoryHelper; + +public class UnsafeMemoryAccessWithStorageMode { + + private final HotSpotDiagnosticMXBean diagnostic = ManagementFactoryHelper.getDiagnosticMXBean(); + private final boolean useCompressedOops; + private final boolean useCompressedKlassPointers; + + private final List collectors = ManagementFactoryHelper.getGarbageCollectorMXBeans(); + + /* + * src/share/vm/utilities/globalDefinitions.hpp + * 363:const uint64_t KlassEncodingMetaspaceMax = (uint64_t(max_juint) + 1) << LogKlassAlignmentInBytes; + * + * src/share/vm/utilities/globalDefinitions.hpp + * 357:const int LogKlassAlignmentInBytes = 3; + */ + private static final long logKlassAlignmentInBytes = 3; + private static final long klassEncodingMetaspaceMax = 0xFFFF_FFFFL + 1 << logKlassAlignmentInBytes; + private static final long klassPointer = (klassEncodingMetaspaceMax - 1) >> logKlassAlignmentInBytes << logKlassAlignmentInBytes; + + private Unsafe unsafe; + private long offset; + + private long data; + + public UnsafeMemoryAccessWithStorageMode() throws Exception { + unsafe = getUnsafe(); + Field f = UnsafeMemoryAccessWithStorageMode.class.getDeclaredField("data"); + offset = unsafe.objectFieldOffset(f); + + { + // Are we running with compressed oops? + String value = diagnostic.getVMOption("UseCompressedOops").getValue(); + useCompressedOops = new Boolean(value).booleanValue(); + } + { + // Are we running with compressed klass pointers? + String value = diagnostic.getVMOption("UseCompressedKlassPointers").getValue(); + useCompressedKlassPointers = new Boolean(value).booleanValue(); + } + } + + private static Unsafe getUnsafe() throws Exception { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return (Unsafe) f.get(null); + } + + private int getCollectionCount() { + int sum = 0; + for (GarbageCollectorMXBean c : collectors) { + sum += c.getCollectionCount(); + } + return sum; + } + + @Test + public void testDefaultOop() throws Exception { + int before = getCollectionCount(); + Object o = new Object(); + unsafe.putObject(this, offset, o, StorageMode.DEFAULT); + Object v = unsafe.getObject(this, offset, StorageMode.DEFAULT); + int after = getCollectionCount(); + // We can't assert on equality if we had a collection. + if (before == after) { + assertEquals(v, o); + } + } + + @Test + public void testUncompressedOop() throws Exception { + int before = getCollectionCount(); + Object o = new Object(); + unsafe.putObject(this, offset, o, StorageMode.UNCOMPRESSED_OOP); + Object v = unsafe.getObject(this, offset, StorageMode.UNCOMPRESSED_OOP); + int after = getCollectionCount(); + // We can't assert on equality if we had a collection. + if (before == after) { + assertEquals(v, o); + } + } + + @Test + public void testCompressedOop() throws Exception { + // We can run this test only with compressed oops turned on because otherwise we hit asserts in the VM. + if (useCompressedOops) { + int before = getCollectionCount(); + Object o = new Object(); + unsafe.putObject(this, offset, o, StorageMode.COMPRESSED_OOP); + Object v = unsafe.getObject(this, offset, StorageMode.COMPRESSED_OOP); + int after = getCollectionCount(); + // We can't assert on equality if we had a collection. + if (before == after) { + assertEquals(v, o); + } + } + } + + @Test + public void testInvalidObjectStorageModes() throws Exception { + for (StorageMode mode : StorageMode.values()) { + switch (mode) { + case DEFAULT: + case UNCOMPRESSED_OOP: + case COMPRESSED_OOP: + // These are valid modes for get/putObject + break; + default: + try { + unsafe.getObject(this, offset, mode); + fail("expected IllegalArgumentException for mode " + mode); + } catch (IllegalArgumentException e) { + // expected + } + try { + unsafe.putObject(this, offset, null, mode); + fail("expected IllegalArgumentException for mode " + mode); + } catch (IllegalArgumentException e) { + // expected + } + break; + } + } + } + + @Test + public void testUncompressedKlass() throws Exception { + long o = klassPointer; + unsafe.putMetadata(this, offset, o, StorageMode.UNCOMPRESSED_KLASS); + long v = unsafe.getMetadata(this, offset, StorageMode.UNCOMPRESSED_KLASS); + assertEquals(v, o); + } + + @Test + public void testCompressedKlass() throws Exception { + // If we run without compressed klass pointers we can only pass in a 32-bit value otherwise we hit asserts in the VM. + long o = useCompressedKlassPointers ? klassPointer : 0x1234_5678L; + unsafe.putMetadata(this, offset, o, StorageMode.COMPRESSED_KLASS); + long v = unsafe.getMetadata(this, offset, StorageMode.COMPRESSED_KLASS); + assertEquals(v, o); + } + + @Test + public void testInvalidMetadataStorageModes() throws Exception { + for (StorageMode mode : StorageMode.values()) { + switch (mode) { + case UNCOMPRESSED_KLASS: + case COMPRESSED_KLASS: + // These are valid modes for get/putMetadata + break; + default: + try { + unsafe.getMetadata(this, offset, mode); + fail("expected IllegalArgumentException for mode " + mode); + } catch (IllegalArgumentException e) { + // expected + } + try { + unsafe.putMetadata(this, offset, 0L, mode); + fail("expected IllegalArgumentException for mode " + mode); + } catch (IllegalArgumentException e) { + // expected + } + break; + } + } + } + +}