# HG changeset patch # User ehelin # Date 1412172541 -7200 # Node ID e0b35f8104a772f0f43f3853e189998835fc5bab # Parent dd2b0f6de283c4e4d1896e2d0458b2702e0ec06d 8049599: MetaspaceGC::_capacity_until_GC can overflow Reviewed-by: jmasa, stefank diff -r dd2b0f6de283 -r e0b35f8104a7 hotspot/src/share/vm/memory/metaspace.cpp --- a/hotspot/src/share/vm/memory/metaspace.cpp Wed Oct 01 12:29:28 2014 +0400 +++ b/hotspot/src/share/vm/memory/metaspace.cpp Wed Oct 01 16:09:01 2014 +0200 @@ -1415,10 +1415,31 @@ return value; } -size_t MetaspaceGC::inc_capacity_until_GC(size_t v) { +bool MetaspaceGC::inc_capacity_until_GC(size_t v, size_t* new_cap_until_GC, size_t* old_cap_until_GC) { 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 aligned max value. + new_value = align_size_down(max_uintx, Metaspace::commit_alignment()); + } + + intptr_t expected = (intptr_t) capacity_until_GC; + intptr_t actual = Atomic::cmpxchg_ptr((intptr_t) new_value, &_capacity_until_GC, expected); + + if (expected != actual) { + return false; + } + + if (new_cap_until_GC != NULL) { + *new_cap_until_GC = new_value; + } + if (old_cap_until_GC != NULL) { + *old_cap_until_GC = capacity_until_GC; + } + return true; } size_t MetaspaceGC::dec_capacity_until_GC(size_t v) { @@ -1518,7 +1539,10 @@ expand_bytes = align_size_up(expand_bytes, Metaspace::commit_alignment()); // Don't expand unless it's significant if (expand_bytes >= MinMetaspaceExpansion) { - size_t new_capacity_until_GC = MetaspaceGC::inc_capacity_until_GC(expand_bytes); + size_t new_capacity_until_GC = 0; + bool succeeded = MetaspaceGC::inc_capacity_until_GC(expand_bytes, &new_capacity_until_GC); + assert(succeeded, "Should always succesfully increment HWM when at safepoint"); + Metaspace::tracer()->report_gc_threshold(capacity_until_GC, new_capacity_until_GC, MetaspaceGCThresholdUpdater::ComputeNewSize); @@ -3321,19 +3345,29 @@ size_t delta_bytes = MetaspaceGC::delta_capacity_until_GC(word_size * BytesPerWord); assert(delta_bytes > 0, "Must be"); - size_t after_inc = MetaspaceGC::inc_capacity_until_GC(delta_bytes); - - // capacity_until_GC might be updated concurrently, must calculate previous value. - size_t before_inc = after_inc - delta_bytes; - - tracer()->report_gc_threshold(before_inc, after_inc, - MetaspaceGCThresholdUpdater::ExpandAndAllocate); - if (PrintGCDetails && Verbose) { - gclog_or_tty->print_cr("Increase capacity to GC from " SIZE_FORMAT - " to " SIZE_FORMAT, before_inc, after_inc); + size_t before = 0; + size_t after = 0; + MetaWord* res; + bool incremented; + + // Each thread increments the HWM at most once. Even if the thread fails to increment + // the HWM, an allocation is still attempted. This is because another thread must then + // have incremented the HWM and therefore the allocation might still succeed. + do { + incremented = MetaspaceGC::inc_capacity_until_GC(delta_bytes, &after, &before); + res = allocate(word_size, mdtype); + } while (!incremented && res == NULL); + + if (incremented) { + tracer()->report_gc_threshold(before, after, + MetaspaceGCThresholdUpdater::ExpandAndAllocate); + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("Increase capacity to GC from " SIZE_FORMAT + " to " SIZE_FORMAT, before, after); + } } - return allocate(word_size, mdtype); + return res; } // Space allocated in the Metaspace. This may diff -r dd2b0f6de283 -r e0b35f8104a7 hotspot/src/share/vm/memory/metaspace.hpp --- a/hotspot/src/share/vm/memory/metaspace.hpp Wed Oct 01 12:29:28 2014 +0400 +++ b/hotspot/src/share/vm/memory/metaspace.hpp Wed Oct 01 16:09:01 2014 +0200 @@ -407,7 +407,9 @@ static void post_initialize(); static size_t capacity_until_GC(); - static size_t inc_capacity_until_GC(size_t v); + static bool inc_capacity_until_GC(size_t v, + size_t* new_cap_until_GC = NULL, + size_t* old_cap_until_GC = NULL); static size_t dec_capacity_until_GC(size_t v); static bool should_concurrent_collect() { return _should_concurrent_collect; } diff -r dd2b0f6de283 -r e0b35f8104a7 hotspot/src/share/vm/prims/whitebox.cpp --- a/hotspot/src/share/vm/prims/whitebox.cpp Wed Oct 01 12:29:28 2014 +0400 +++ b/hotspot/src/share/vm/prims/whitebox.cpp Wed Oct 01 16:09:01 2014 +0200 @@ -820,6 +820,33 @@ MetadataFactory::free_array(cld, (Array*)(uintptr_t)addr); WB_END +WB_ENTRY(jlong, WB_IncMetaspaceCapacityUntilGC(JNIEnv* env, jobject wb, jlong inc)) + if (inc < 0) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + err_msg("WB_IncMetaspaceCapacityUntilGC: inc is negative: " JLONG_FORMAT, inc)); + } + + jlong max_size_t = (jlong) ((size_t) -1); + if (inc > max_size_t) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + err_msg("WB_IncMetaspaceCapacityUntilGC: inc does not fit in size_t: " JLONG_FORMAT, inc)); + } + + size_t new_cap_until_GC = 0; + size_t aligned_inc = align_size_down((size_t) inc, Metaspace::commit_alignment()); + bool success = MetaspaceGC::inc_capacity_until_GC(aligned_inc, &new_cap_until_GC); + if (!success) { + THROW_MSG_0(vmSymbols::java_lang_IllegalStateException(), + "WB_IncMetaspaceCapacityUntilGC: could not increase capacity until GC " + "due to contention with another thread"); + } + return (jlong) new_cap_until_GC; +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) { @@ -991,6 +1018,8 @@ 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 }, diff -r dd2b0f6de283 -r e0b35f8104a7 hotspot/test/gc/metaspace/TestCapacityUntilGCWrapAround.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/metaspace/TestCapacityUntilGCWrapAround.java Wed Oct 01 16:09:01 2014 +0200 @@ -0,0 +1,59 @@ +/* + * 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(); + // Now force possible overflow of capacity_until_GC. + long after = wb.incMetaspaceCapacityUntilGC(MAX_UINT); + + Asserts.assertGTE(after, before, + "Increasing with MAX_UINT should not cause wrap around: " + after + " < " + before); + Asserts.assertLTE(after, MAX_UINT, + "Increasing with MAX_UINT should not cause value larger than MAX_UINT:" + after); + } + } +} diff -r dd2b0f6de283 -r e0b35f8104a7 hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java --- a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Wed Oct 01 12:29:28 2014 +0400 +++ b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java Wed Oct 01 16:09:01 2014 +0200 @@ -152,6 +152,8 @@ 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 Young GC public native void youngGC();