# HG changeset patch # User ehelin # Date 1408527940 -7200 # Wed Aug 20 11:45:40 2014 +0200 # Node ID f1017f8d2f1186ee1c15a6cb97f76b4dc623c555 # Parent d5101d894b879513947adf716756b514c4959d6b 8049599: MetaspaceGC::_capacity_until_GC can overflow diff --git a/src/share/vm/memory/metaspace.cpp b/src/share/vm/memory/metaspace.cpp --- a/src/share/vm/memory/metaspace.cpp +++ b/src/share/vm/memory/metaspace.cpp @@ -1410,17 +1410,39 @@ size_t MetaspaceGC::capacity_until_GC() size_t value = (size_t)OrderAccess::load_ptr_acquire(&_capacity_until_GC); assert(value >= MetaspaceSize, "Not initialied properly?"); return value; } size_t MetaspaceGC::inc_capacity_until_GC(size_t v) { assert_is_size_aligned(v, Metaspace::commit_alignment()); - return (size_t)Atomic::add_ptr(v, &_capacity_until_GC); + size_t capacity_until_GC = (size_t) _capacity_until_GC; + size_t new_value = capacity_until_GC + v; + + if (new_value < capacity_until_GC) { + // the addition wrapped around, set new_value to max value + new_value = max_uintx; + } + + bool cas_succeeded = false; + size_t num_retries = 5; // just to limit the number of CAS attempts + for (size_t i = 0; i < num_retries; i++) { + intptr_t cmp = (intptr_t) capacity_until_GC; + intptr_t swap = (intptr_t) new_value; + intptr_t prev = Atomic::cmpxchg_ptr(swap, &_capacity_until_GC, cmp); + + if (prev == cmp) { + cas_succeeded = true; + break; + } + } + + // if the CAS did not succeed, just return the current value + return cas_succeeded ? new_value : _capacity_until_GC; } size_t MetaspaceGC::dec_capacity_until_GC(size_t v) { assert_is_size_aligned(v, Metaspace::commit_alignment()); return (size_t)Atomic::add_ptr(-(intptr_t)v, &_capacity_until_GC); } diff --git a/src/share/vm/prims/whitebox.cpp b/src/share/vm/prims/whitebox.cpp --- a/src/share/vm/prims/whitebox.cpp +++ b/src/share/vm/prims/whitebox.cpp @@ -781,16 +781,35 @@ WB_ENTRY(void, WB_FreeMetaspace(JNIEnv* oop class_loader_oop = JNIHandles::resolve(class_loader); ClassLoaderData* cld = class_loader_oop != NULL ? java_lang_ClassLoader::loader_data(class_loader_oop) : ClassLoaderData::the_null_class_loader_data(); MetadataFactory::free_array(cld, (Array*)(uintptr_t)addr); WB_END +WB_ENTRY(jlong, WB_IncMetaspaceCapacityUntilGC(JNIEnv* env, jobject wb, jlong size)) + if (size < 0) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + err_msg("WB_IncMetaspaceCapacityUntilGC: size is negative: " JLONG_FORMAT, size)); + } + + if (size > (jlong) ((size_t) -1)) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + err_msg("WB_IncMetaspaceCapacityUntilGC: size does not fit in size_t: " JLONG_FORMAT, size)); + } + + size_t new_val = MetaspaceGC::inc_capacity_until_GC(align_size_down((size_t) size, Metaspace::commit_alignment())); + return (jlong) new_val; +WB_END + +WB_ENTRY(jlong, WB_MetaspaceCapacityUntilGC(JNIEnv* env, jobject wb)) + return (jlong) MetaspaceGC::capacity_until_GC(); +WB_END + //Some convenience methods to deal with objects from java int WhiteBox::offset_for_field(const char* field_name, oop object, Symbol* signature_symbol) { assert(field_name != NULL && strlen(field_name) > 0, "Field name not valid"); Thread* THREAD = Thread::current(); //Get the class of our object Klass* arg_klass = object->klass(); @@ -919,16 +938,18 @@ static JNINativeMethod methods[] = { (void*)&WB_GetStringVMFlag}, {CC"isInStringTable", CC"(Ljava/lang/String;)Z", (void*)&WB_IsInStringTable }, {CC"fullGC", CC"()V", (void*)&WB_FullGC }, {CC"readReservedMemory", CC"()V", (void*)&WB_ReadReservedMemory }, {CC"allocateMetaspace", CC"(Ljava/lang/ClassLoader;J)J", (void*)&WB_AllocateMetaspace }, {CC"freeMetaspace", CC"(Ljava/lang/ClassLoader;JJ)V", (void*)&WB_FreeMetaspace }, + {CC"incMetaspaceCapacityUntilGC", CC"(J)J", (void*)&WB_IncMetaspaceCapacityUntilGC }, + {CC"metaspaceCapacityUntilGC", CC"()J", (void*)&WB_MetaspaceCapacityUntilGC }, {CC"getCPUFeatures", CC"()Ljava/lang/String;", (void*)&WB_GetCPUFeatures }, {CC"getNMethod", CC"(Ljava/lang/reflect/Executable;Z)[Ljava/lang/Object;", (void*)&WB_GetNMethod }, {CC"getThreadStackSize", CC"()J", (void*)&WB_GetThreadStackSize }, {CC"getThreadRemainingStackSize", CC"()J", (void*)&WB_GetThreadRemainingStackSize }, }; #undef CC diff --git a/test/gc/metaspace/TestCapacityUntilGCWrapAround.java b/test/gc/metaspace/TestCapacityUntilGCWrapAround.java new file mode 100644 --- /dev/null +++ b/test/gc/metaspace/TestCapacityUntilGCWrapAround.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, 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 + * @key gc + * @bug 8049831 + * @library /testlibrary /testlibrary/whitebox + * @build TestCapacityUntilGCWrapAround + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI TestCapacityUntilGCWrapAround + */ + +import sun.hotspot.WhiteBox; + +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.Platform; + +public class TestCapacityUntilGCWrapAround { + private static long MB = 1024 * 1024; + private static long GB = 1024 * MB; + private static long MAX_UINT = 4 * GB - 1; // On 32-bit platforms + + public static void main(String[] args) { + if (Platform.is32bit()) { + WhiteBox wb = WhiteBox.getWhiteBox(); + + long before = wb.metaspaceCapacityUntilGC(); + long after = wb.incMetaspaceCapacityUntilGC(MAX_UINT); + + Asserts.assertGTE(after, before, + String.format("Increasing with MAX_UINT should not cause wrap around: %d < %d", after, before)); + Asserts.assertEQ(after, MAX_UINT, + String.format("Increasing with MAX_UINT should set Metaspace::_capacityUntilGC to max_uint, not: %d", after)); + } + } +} diff --git a/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java b/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java --- a/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java +++ b/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java @@ -144,16 +144,18 @@ public class WhiteBox { // Intered strings public native boolean isInStringTable(String str); // Memory public native void readReservedMemory(); public native long allocateMetaspace(ClassLoader classLoader, long size); public native void freeMetaspace(ClassLoader classLoader, long addr, long size); + public native long incMetaspaceCapacityUntilGC(long increment); + public native long metaspaceCapacityUntilGC(); // force Full GC public native void fullGC(); // Tests on ReservedSpace/VirtualSpace classes public native int stressVirtualSpaceResize(long reservedSpaceSize, long magnitude, long iterations); public native void runMemoryUnitTests(); public native void readFromNoaccessArea();