Skip to content

Commit 82790b0

Browse files
committed
8295724: VirtualMachineError: Out of space in CodeCache for method handle intrinsic
8298947: compiler/codecache/MHIntrinsicAllocFailureTest.java fails intermittently Reviewed-by: kvn, mbaesken Backport-of: cd2182a9967917e733e486d918e9aeba3bd35ee8
1 parent 633a763 commit 82790b0

File tree

4 files changed

+147
-3
lines changed

4 files changed

+147
-3
lines changed

src/hotspot/share/code/nmethod.cpp

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,42 @@ void nmethod::init_defaults() {
451451
#endif
452452
}
453453

454+
#ifdef ASSERT
455+
class CheckForOopsClosure : public OopClosure {
456+
bool _found_oop = false;
457+
public:
458+
virtual void do_oop(oop* o) { _found_oop = true; }
459+
virtual void do_oop(narrowOop* o) { _found_oop = true; }
460+
bool found_oop() { return _found_oop; }
461+
};
462+
class CheckForMetadataClosure : public MetadataClosure {
463+
bool _found_metadata = false;
464+
Metadata* _ignore = nullptr;
465+
public:
466+
CheckForMetadataClosure(Metadata* ignore) : _ignore(ignore) {}
467+
virtual void do_metadata(Metadata* md) { if (md != _ignore) _found_metadata = true; }
468+
bool found_metadata() { return _found_metadata; }
469+
};
470+
471+
static void assert_no_oops_or_metadata(nmethod* nm) {
472+
if (nm == nullptr) return;
473+
assert(nm->oop_maps() == nullptr, "expectation");
474+
475+
CheckForOopsClosure cfo;
476+
nm->oops_do(&cfo);
477+
assert(!cfo.found_oop(), "no oops allowed");
478+
479+
// We allow an exception for the own Method, but require its class to be permanent.
480+
Method* own_method = nm->method();
481+
CheckForMetadataClosure cfm(/* ignore reference to own Method */ own_method);
482+
nm->metadata_do(&cfm);
483+
assert(!cfm.found_metadata(), "no metadata allowed");
484+
485+
assert(own_method->method_holder()->class_loader_data()->is_permanent_class_loader_data(),
486+
"Method's class needs to be permanent");
487+
}
488+
#endif
489+
454490
nmethod* nmethod::new_native_nmethod(const methodHandle& method,
455491
int compile_id,
456492
CodeBuffer *code_buffer,
@@ -470,14 +506,19 @@ nmethod* nmethod::new_native_nmethod(const methodHandle& method,
470506
CodeOffsets offsets;
471507
offsets.set_value(CodeOffsets::Verified_Entry, vep_offset);
472508
offsets.set_value(CodeOffsets::Frame_Complete, frame_complete);
473-
nm = new (native_nmethod_size, CompLevel_none)
509+
510+
// MH intrinsics are dispatch stubs which are compatible with NonNMethod space.
511+
// IsUnloadingBehaviour::is_unloading needs to handle them separately.
512+
bool allow_NonNMethod_space = method->can_be_allocated_in_NonNMethod_space();
513+
nm = new (native_nmethod_size, allow_NonNMethod_space)
474514
nmethod(method(), compiler_none, native_nmethod_size,
475515
compile_id, &offsets,
476516
code_buffer, frame_size,
477517
basic_lock_owner_sp_offset,
478518
basic_lock_sp_offset,
479519
oop_maps);
480-
NOT_PRODUCT(if (nm != NULL) native_nmethod_stats.note_native_nmethod(nm));
520+
DEBUG_ONLY( if (allow_NonNMethod_space) assert_no_oops_or_metadata(nm); )
521+
NOT_PRODUCT(if (nm != NULL) native_nmethod_stats.note_native_nmethod(nm));
481522
}
482523

483524
if (nm != NULL) {
@@ -710,6 +751,14 @@ void* nmethod::operator new(size_t size, int nmethod_size, int comp_level) throw
710751
return CodeCache::allocate(nmethod_size, CodeCache::get_code_blob_type(comp_level));
711752
}
712753

754+
void* nmethod::operator new(size_t size, int nmethod_size, bool allow_NonNMethod_space) throw () {
755+
// Try MethodNonProfiled and MethodProfiled.
756+
void* return_value = CodeCache::allocate(nmethod_size, CodeBlobType::MethodNonProfiled);
757+
if (return_value != nullptr || !allow_NonNMethod_space) return return_value;
758+
// Try NonNMethod or give up.
759+
return CodeCache::allocate(nmethod_size, CodeBlobType::NonNMethod);
760+
}
761+
713762
nmethod::nmethod(
714763
Method* method,
715764
CompilerType type,
@@ -1780,7 +1829,10 @@ bool nmethod::is_unloading() {
17801829
// oops in the CompiledMethod, by calling oops_do on it.
17811830
state_unloading_cycle = current_cycle;
17821831

1783-
if (is_zombie()) {
1832+
if (is_zombie() || method()->can_be_allocated_in_NonNMethod_space()) {
1833+
// When the nmethod is in NonNMethod space, we may reach here without IsUnloadingBehaviour.
1834+
// However, we only allow this for special methods which never get unloaded.
1835+
17841836
// Zombies without calculated unloading epoch are never unloading due to GC.
17851837

17861838
// There are no races where a previously observed is_unloading() nmethod

src/hotspot/share/code/nmethod.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ class nmethod : public CompiledMethod {
324324

325325
// helper methods
326326
void* operator new(size_t size, int nmethod_size, int comp_level) throw();
327+
// For method handle intrinsics: Try MethodNonProfiled, MethodProfiled and NonNMethod.
328+
// Attention: Only allow NonNMethod space for special nmethods which don't need to be
329+
// findable by nmethod iterators! In particular, they must not contain oops!
330+
void* operator new(size_t size, int nmethod_size, bool allow_NonNMethod_space) throw();
327331

328332
const char* reloc_string_for(u_char* begin, u_char* end);
329333

src/hotspot/share/oops/method.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,8 @@ class Method : public Metadata {
720720
static methodHandle make_method_handle_intrinsic(vmIntrinsicID iid, // _invokeBasic, _linkToVirtual
721721
Symbol* signature, //anything at all
722722
TRAPS);
723+
// Some special methods don't need to be findable by nmethod iterators and are permanent.
724+
bool can_be_allocated_in_NonNMethod_space() const { return is_method_handle_intrinsic(); }
723725
static Klass* check_non_bcp_klass(Klass* klass);
724726

725727
enum {
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2022 SAP SE. All rights reserved.ights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* This code is distributed in the hope that it will be useful, but WITHOUT
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13+
* version 2 for more details (a copy is included in the LICENSE file that
14+
* accompanied this code).
15+
*
16+
* You should have received a copy of the GNU General Public License version
17+
* 2 along with this work; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19+
*
20+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21+
* or visit www.oracle.com if you need additional information or have any
22+
* questions.
23+
*/
24+
25+
/*
26+
* @test MHIntrinsicAllocFailureTest
27+
* @bug 8295724
28+
* @requires vm.compMode == "Xmixed"
29+
* @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true
30+
* @requires vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4
31+
* @summary test allocation failure of method handle intrinsic in profiled/non-profiled space
32+
* @library /test/lib
33+
* @modules java.base/jdk.internal.misc
34+
* java.management
35+
*
36+
* @build jdk.test.whitebox.WhiteBox
37+
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
38+
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
39+
* -XX:+WhiteBoxAPI -XX:CompileCommand=compileonly,null::*
40+
* -XX:ReservedCodeCacheSize=16m -XX:+SegmentedCodeCache
41+
* compiler.codecache.MHIntrinsicAllocFailureTest
42+
*/
43+
44+
package compiler.codecache;
45+
46+
import jdk.test.lib.Asserts;
47+
import jdk.test.whitebox.WhiteBox;
48+
import jdk.test.whitebox.code.BlobType;
49+
50+
import java.lang.management.MemoryPoolMXBean;
51+
52+
public class MHIntrinsicAllocFailureTest {
53+
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
54+
55+
private interface TestInterface {
56+
int testMethod(int a, int b, Object c);
57+
}
58+
59+
private static void fillCodeCacheSegment(BlobType type) {
60+
// Fill with large blobs.
61+
MemoryPoolMXBean bean = type.getMemoryPool();
62+
int size = (int) (bean.getUsage().getMax() >> 7);
63+
while (WHITE_BOX.allocateCodeBlob(size, type.id) != 0) {}
64+
// Fill rest with minimal blobs.
65+
while (WHITE_BOX.allocateCodeBlob(1, type.id) != 0) {}
66+
}
67+
68+
public static void main(String[] args) {
69+
// Lock compilation to be able to better control code cache space
70+
WHITE_BOX.lockCompilation();
71+
fillCodeCacheSegment(BlobType.MethodNonProfiled);
72+
fillCodeCacheSegment(BlobType.MethodProfiled);
73+
// JIT compilers should be off, now.
74+
Asserts.assertNotEquals(WHITE_BOX.getCompilationActivityMode(), 1);
75+
System.out.println("Code cache segments for non-profiled and profiled nmethods are full.");
76+
// Generate and use a MH itrinsic. Should not trigger one of the following:
77+
// - VirtualMachineError: Out of space in CodeCache for method handle intrinsic
78+
// - InternalError: java.lang.NoSuchMethodException: no such method:
79+
// java.lang.invoke.MethodHandle.linkToStatic(int,int,Object,MemberName)int/invokeStatic
80+
TestInterface add2ints = (a, b, c) -> a + b;
81+
System.out.println("Result of lambda expression: " + add2ints.testMethod(1, 2, null));
82+
// Let GC check the code cache.
83+
WHITE_BOX.unlockCompilation();
84+
WHITE_BOX.fullGC();
85+
}
86+
}

0 commit comments

Comments
 (0)