diff --git a/src/share/vm/memory/metaspace.cpp b/src/share/vm/memory/metaspace.cpp index 169557399c1e24c2740be185ef9458778594f4c6..8fb833b54269b20b63ff4ee833d1096c38d909af 100644 --- a/src/share/vm/memory/metaspace.cpp +++ b/src/share/vm/memory/metaspace.cpp @@ -1423,6 +1423,17 @@ size_t MetaspaceGC::dec_capacity_until_GC(size_t v) { return (size_t)Atomic::add_ptr(-(intptr_t)v, &_capacity_until_GC); } +void MetaspaceGC::initialize() { + // Set the high-water mark to MaxMetapaceSize during VM initializaton since + // we can't do a GC during initialization. + _capacity_until_GC = MaxMetaspaceSize; +} + +void MetaspaceGC::post_initialize() { + // Reset the high-water mark once the VM initialization is done. + _capacity_until_GC = MAX2(MetaspaceAux::committed_bytes(), MetaspaceSize); +} + bool MetaspaceGC::can_expand(size_t word_size, bool is_class) { // Check if the compressed class space is full. if (is_class && Metaspace::using_class_space()) { @@ -1443,21 +1454,13 @@ bool MetaspaceGC::can_expand(size_t word_size, bool is_class) { size_t MetaspaceGC::allowed_expansion() { size_t committed_bytes = MetaspaceAux::committed_bytes(); - - size_t left_until_max = MaxMetaspaceSize - committed_bytes; - - // Always grant expansion if we are initiating the JVM, - // or if the GC_locker is preventing GCs. - if (!is_init_completed() || GC_locker::is_active_and_needs_gc()) { - return left_until_max / BytesPerWord; - } - size_t capacity_until_gc = capacity_until_GC(); - if (capacity_until_gc <= committed_bytes) { - return 0; - } + assert(capacity_until_gc >= committed_bytes, + err_msg("capacity_until_gc: " SIZE_FORMAT " < committed_bytes: " SIZE_FORMAT, + capacity_until_gc, committed_bytes)); + size_t left_until_max = MaxMetaspaceSize - committed_bytes; size_t left_until_GC = capacity_until_gc - committed_bytes; size_t left_to_commit = MIN2(left_until_GC, left_until_max); @@ -1469,7 +1472,15 @@ void MetaspaceGC::compute_new_size() { uint current_shrink_factor = _shrink_factor; _shrink_factor = 0; - const size_t used_after_gc = MetaspaceAux::capacity_bytes(); + // Using committed_bytes() for used_after_gc is an overestimation, since the + // chunk free lists are included in committed_bytes() and the memory in an + // un-fragmented chunk free list is available for future allocations. + // However, if the chunk free lists becomes fragmented, then the memory may + // not be available for future allocations and the memory is therefore "in use". + // Including the chunk free lists in the definition of "in use" is therefore + // necessary. Not including the chunk free lists can cause capacity_until_GC to + // shrink below committed_bytes() and this has caused serious bugs in the past. + const size_t used_after_gc = MetaspaceAux::committed_bytes(); const size_t capacity_until_GC = MetaspaceGC::capacity_until_GC(); const double minimum_free_percentage = MinMetaspaceFreeRatio / 100.0; @@ -3093,6 +3104,8 @@ void Metaspace::ergo_initialize() { } void Metaspace::global_initialize() { + MetaspaceGC::initialize(); + // Initialize the alignment for shared spaces. int max_alignment = os::vm_page_size(); size_t cds_total = 0; @@ -3200,10 +3213,13 @@ void Metaspace::global_initialize() { } } - MetaspaceGC::initialize(); _tracer = new MetaspaceTracer(); } +void Metaspace::post_initialize() { + MetaspaceGC::post_initialize(); +} + Metachunk* Metaspace::get_initialization_chunk(MetadataType mdtype, size_t chunk_word_size, size_t chunk_bunch) { diff --git a/src/share/vm/memory/metaspace.hpp b/src/share/vm/memory/metaspace.hpp index 22e21b80d3d172e66ea2b42c8cd8422a8d6cecb3..baa25cd6d3510dbcb9b9d363ad33c960e298b5b6 100644 --- a/src/share/vm/memory/metaspace.hpp +++ b/src/share/vm/memory/metaspace.hpp @@ -208,6 +208,7 @@ class Metaspace : public CHeapObj { static void ergo_initialize(); static void global_initialize(); + static void post_initialize(); static size_t first_chunk_word_size() { return _first_chunk_word_size; } static size_t first_class_chunk_word_size() { return _first_class_chunk_word_size; } @@ -398,7 +399,8 @@ class MetaspaceGC : AllStatic { public: - static void initialize() { _capacity_until_GC = MetaspaceSize; } + static void initialize(); + static void post_initialize(); static size_t capacity_until_GC(); static size_t inc_capacity_until_GC(size_t v); diff --git a/src/share/vm/runtime/thread.cpp b/src/share/vm/runtime/thread.cpp index 39f12a5b0e6f9a96706f10ed00c714ef6d59408f..47d421efbccd4964e09bea42c6fd1b901f2cabc1 100644 --- a/src/share/vm/runtime/thread.cpp +++ b/src/share/vm/runtime/thread.cpp @@ -3574,6 +3574,8 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { // debug stuff, that does not work until all basic classes have been initialized. set_init_completed(); + Metaspace::post_initialize(); + #ifndef USDT2 HS_DTRACE_PROBE(hotspot, vm__init__end); #else /* USDT2 */ diff --git a/test/gc/metaspace/TestMetaspaceInitialization.java b/test/gc/metaspace/TestMetaspaceInitialization.java new file mode 100644 index 0000000000000000000000000000000000000000..c7bbcec0829fa5d5e0d2177b49cfe6dd474f97bc --- /dev/null +++ b/test/gc/metaspace/TestMetaspaceInitialization.java @@ -0,0 +1,48 @@ +/* + * 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. + */ + +import java.util.ArrayList; + +/* @test TestMetaspaceInitialization + * @bug 8042933 + * @summary Tests to initialize metaspace with a very low MetaspaceSize + * @library /testlibrary + * @run main/othervm -XX:MetaspaceSize=2m TestMetaspaceInitialization + */ +public class TestMetaspaceInitialization { + private class Internal { + public int x; + public Internal(int x) { + this.x = x; + } + } + + private void test() { + ArrayList l = new ArrayList<>(); + l.add(new Internal(17)); + } + + public static void main(String[] args) { + new TestMetaspaceInitialization().test(); + } +}