JDK-8231895 : Avoid String allocations in JVM_FindLoadedClass
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 14
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2019-10-04
  • Updated: 2019-10-09
  • Resolved: 2019-10-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 14
14 b18Fixed
Description
JVM_FindLoadedClass takes the given jstring class name and calls internalize_classname - which returns a new java String after converting '.' to '/' in the original one. We then turn that string into an UTF-8 string. This means we're allocating a String object on heap, which we then instantly throw away, and unless I'm missing something we could just get rid of this intermediary allocation.

internalize_classname is only used in this one place, so it should be inlined and simplified to remove a String allocation by doing the conversion to utf-8 first, then update that in place. 
Comments
URL: https://hg.openjdk.java.net/jdk/jdk/rev/ccbb5a2bf3ab User: redestad Date: 2019-10-07 09:56:10 +0000
07-10-2019

@Benchmark public Class<?> forName() throws Throwable { return Class.forName(getClass().getModule(), "java.util.Spliterator"); } Benchmark Mode Cnt Score Error Units Clazz.forName avgt 5 543.572 �� 73.356 ns/op Clazz.forName:��gc.alloc.rate.norm avgt 5 328.000 �� 0.001 B/op Clazz.forName avgt 5 397.593 �� 30.581 ns/op Clazz.forName:��gc.alloc.rate.norm avgt 5 248.000 �� 0.001 B/op
05-10-2019

Potential fix: diff -r d873ce07465d src/hotspot/share/classfile/javaClasses.hpp --- a/src/hotspot/share/classfile/javaClasses.hpp Thu Oct 03 13:45:08 2019 +0200 +++ b/src/hotspot/share/classfile/javaClasses.hpp Fri Oct 04 17:43:13 2019 +0200 @@ -202,7 +202,6 @@ // Conversion between '.' and '/' formats static Handle externalize_classname(Handle java_string, TRAPS) { return char_converter(java_string, '/', '.', THREAD); } - static Handle internalize_classname(Handle java_string, TRAPS) { return char_converter(java_string, '.', '/', THREAD); } // Conversion static Symbol* as_symbol(oop java_string); diff -r d873ce07465d src/hotspot/share/prims/jvm.cpp --- a/src/hotspot/share/prims/jvm.cpp Thu Oct 03 13:45:08 2019 +0200 +++ b/src/hotspot/share/prims/jvm.cpp Fri Oct 04 17:43:13 2019 +0200 @@ -990,13 +990,21 @@ ResourceMark rm(THREAD); Handle h_name (THREAD, JNIHandles::resolve_non_null(name)); - Handle string = java_lang_String::internalize_classname(h_name, CHECK_NULL); - - const char* str = java_lang_String::as_utf8_string(string()); + const char* str = java_lang_String::as_utf8_string(h_name()); + // Sanity check, don't expect null if (str == NULL) return NULL; - const int str_len = (int)strlen(str); + // Internalize the string, converting '.' to '/' in string. + char* p = (char*)str; + while (*p != '\0') { + if (*p == '.') { + *p = '/'; + } + p++; + } + + const int str_len = (int)(p - str); if (str_len > Symbol::max_length()) { // It's impossible to create this class; the name cannot fit // into the constant pool.
05-10-2019