--- /dev/null 2013-11-25 16:34:39.360817680 +0100 +++ new/test/compiler/uncommontrap/TestSpecTrapClassUnloading.java 2014-02-21 09:27:53.192413040 +0100 @@ -0,0 +1,97 @@ +/* + * 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 + * @bug 8031752 + * @summary speculative traps need to be cleaned up at GC + * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:TypeProfileLevel=222 -XX:CompileCommand=exclude,java.lang.reflect.Method::invoke -XX:CompileCommand=exclude,sun.reflect.DelegatingMethodAccessorImpl::invoke -Xmx1M TestSpecTrapClassUnloading + * + */ + +import java.lang.reflect.Method; + +public class TestSpecTrapClassUnloading { + static class B { + final public boolean m(Object o) { + if (o.getClass() == B.class) { + return true; + } + return false; + } + } + + static class MemoryChunk { + MemoryChunk other; + long[] array; + MemoryChunk(MemoryChunk other) { + other = other; + array = new long[1024 * 1024 * 1024]; + } + } + + static void m1(B b, Object o) { + b.m(o); + } + + static void m2(B b, Object o) { + b.m(o); + } + + public static void main(String[] args) throws Exception { + Method m = B.class.getMethod("m", Object.class); + Object o = new Object(); + B b = new B(); + + // add speculative trap in B.m() for m1 + for (int i = 0; i < 20000; i++) { + m1(b, b); + } + m1(b, o); + + // add speculative trap in B.m() for code generated by reflection + for (int i = 0; i < 20000; i++) { + m.invoke(b, b); + } + m.invoke(b, o); + + m = null; + + // add speculative trap in B.m() for m2 + for (int i = 0; i < 20000; i++) { + m2(b, b); + } + m2(b, o); + + // Exhaust memory which causes the code generated by + // reflection to be unloaded but B.m() is not. + MemoryChunk root = null; + try { + while (true) { + root = new MemoryChunk(root); + } + } catch(OutOfMemoryError e) { + root = null; + } + } +} --- old/src/share/vm/oops/methodData.cpp 2014-02-21 09:27:53.373279220 +0100 +++ new/src/share/vm/oops/methodData.cpp 2014-02-21 09:27:53.213273599 +0100 @@ -1517,17 +1517,74 @@ return m->is_compiled_lambda_form(); } -void MethodData::clean_method_data(BoolObjectClosure* is_alive) { - for (ProfileData* data = first_data(); - is_valid(data); - data = next_data(data)) { - data->clean_weak_klass_links(is_alive); +void MethodData::clean_extra_data_helper(DataLayout* dp, int shift, bool reset) { + if (shift == 0) { + return; } - ParametersTypeData* parameters = parameters_type_data(); - if (parameters != NULL) { - parameters->clean_weak_klass_links(is_alive); + if (!reset) { + // Move all cells of trap entry at dp left by "shift" cells + intptr_t* start = (intptr_t*)dp; + intptr_t* end = (intptr_t*)next_extra(dp); + for (intptr_t* ptr = start; ptr < end; ptr++) { + *(ptr-shift) = *ptr; + } + } else { + // Reset "shift" cells stopping at dp + intptr_t* start = ((intptr_t*)dp) - shift; + intptr_t* end = (intptr_t*)dp; + for (intptr_t* ptr = start; ptr < end; ptr++) { + *ptr = 0; + } } +} +// Remove SpeculativeTrapData entries that reference an unloaded +// method +void MethodData::clean_extra_data(BoolObjectClosure* is_alive) { + DataLayout* dp = extra_data_base(); + DataLayout* end = extra_data_limit(); + + int shift = 0; + for (; dp < end; dp = next_extra(dp)) { + switch(dp->tag()) { + case DataLayout::speculative_trap_data_tag: { + SpeculativeTrapData* data = new SpeculativeTrapData(dp); + Method* m = data->method(); + assert(m != NULL, "should have a method"); + if (!m->method_holder()->is_loader_alive(is_alive)) { + // "shift" accumulates the number of cells for dead + // SpeculativeTrapData entries that have been seen so + // far. Following entries must be shifted left by that many + // cells to remove the dead SpeculativeTrapData entries. + shift += (int)((intptr_t*)next_extra(dp) - (intptr_t*)dp); + } else { + // Shift this entry left if it follows dead + // SpeculativeTrapData entries + clean_extra_data_helper(dp, shift); + } + break; + } + case dp->DataLayout::bit_data_tag: + // Shift this entry left if it follows dead SpeculativeTrapData + // entries + clean_extra_data_helper(dp, shift); + continue; + case DataLayout::no_tag: + case DataLayout::arg_info_data_tag: + // We are at end of the live trap entries. The previous "shift" + // cells contain entries that are either dead or were shifted + // left. They need to be reset to no_tag + clean_extra_data_helper(dp, shift, true); + return; + default: + fatal(err_msg("unexpected tag %d", dp->tag())); + } + } +} + +// Verify there's no unloaded method referenced by a +// SpeculativeTrapData entry +void MethodData::verify_extra_data_clean(BoolObjectClosure* is_alive) { #ifdef ASSERT DataLayout* dp = extra_data_base(); DataLayout* end = extra_data_limit(); @@ -1551,3 +1608,18 @@ } #endif } + +void MethodData::clean_method_data(BoolObjectClosure* is_alive) { + for (ProfileData* data = first_data(); + is_valid(data); + data = next_data(data)) { + data->clean_weak_klass_links(is_alive); + } + ParametersTypeData* parameters = parameters_type_data(); + if (parameters != NULL) { + parameters->clean_weak_klass_links(is_alive); + } + + clean_extra_data(is_alive); + verify_extra_data_clean(is_alive); +} --- old/src/share/vm/oops/methodData.hpp 2014-02-21 09:27:53.375005774 +0100 +++ new/src/share/vm/oops/methodData.hpp 2014-02-21 09:27:53.235110900 +0100 @@ -2180,6 +2180,10 @@ static bool profile_parameters_jsr292_only(); static bool profile_all_parameters(); + void clean_extra_data(BoolObjectClosure* is_alive); + void clean_extra_data_helper(DataLayout* dp, int shift, bool reset = false); + void verify_extra_data_clean(BoolObjectClosure* is_alive); + public: static int header_size() { return sizeof(MethodData)/wordSize;