Hi Mandy, thanks for looking into this. Uunfortunately your fix only helps to fix "java -version" :(
Running even a minimal HelloWorld will still fail with the following stack trace which is still caused by the same EmptyStackException: Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.ExceptionInInitializerError at sun.misc.Unsafe.ensureClassInitialized(Native Method) at sun.misc.SharedSecrets.getJavaUtilZipFileAccess(SharedSecrets.java:158) at sun.misc.URLClassPath$JarLoader.<clinit>(URLClassPath.java:765) at sun.misc.URLClassPath$3.run(URLClassPath.java:530) at sun.misc.URLClassPath$3.run(URLClassPath.java:520) at java.security.AccessController.doPrivileged(Native Method) at sun.misc.URLClassPath.getLoader(URLClassPath.java:519) at sun.misc.URLClassPath.getLoader(URLClassPath.java:492) at sun.misc.URLClassPath.getNextLoader(URLClassPath.java:457) at sun.misc.URLClassPath.access$100(URLClassPath.java:64) at sun.misc.URLClassPath$1.next(URLClassPath.java:239) at sun.misc.URLClassPath$1.hasMoreElements(URLClassPath.java:250) at java.net.URLClassLoader$3$1.run(URLClassLoader.java:601) at java.net.URLClassLoader$3$1.run(URLClassLoader.java:599) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader$3.next(URLClassLoader.java:598) at java.net.URLClassLoader$3.hasMoreElements(URLClassLoader.java:623) at sun.misc.CompoundEnumeration.next(CompoundEnumeration.java:45) at sun.misc.CompoundEnumeration.hasMoreElements(CompoundEnumeration.java:54) at sun.misc.CompoundEnumeration.next(CompoundEnumeration.java:45) at sun.misc.CompoundEnumeration.hasMoreElements(CompoundEnumeration.java:54) at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:354) at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:393) at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:474) at java.nio.charset.Charset$1.getNext(Charset.java:350) at java.nio.charset.Charset$1.hasNext(Charset.java:365) at java.nio.charset.Charset$2.run(Charset.java:410) at java.nio.charset.Charset$2.run(Charset.java:407) at java.security.AccessController.doPrivileged(Native Method) at java.nio.charset.Charset.lookupViaProviders(Charset.java:406) at java.nio.charset.Charset.lookup2(Charset.java:477) at java.nio.charset.Charset.lookup(Charset.java:464) at java.nio.charset.Charset.isSupported(Charset.java:505) at sun.launcher.LauncherHelper.makePlatformString(LauncherHelper.java:580) Caused by: java.util.EmptyStackException at java.util.Stack.peek(Stack.java:102) at java.lang.ClassLoader$NativeLibrary.getFromClass(ClassLoader.java:1759) at java.lang.ClassLoader$NativeLibrary.findBuiltinLib(Native Method) at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1870) at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1843) at java.lang.Runtime.loadLibrary0(Runtime.java:870) at java.lang.System.loadLibrary(System.java:1122) at java.util.zip.ZipFile.<clinit>(ZipFile.java:86) ... 34 more It's obvious that the way jni_FindClass is looking for the class context by calling the NativeLibrary::getFromClass method is hacky but I think that the proposed fix is the quite simple and non-intrusive. And we probably don't want a complete rework of this code for 8 anyway. So why not fix it the proposed way in 8 and 9 for now? That will still leave us time to come up with a better clean-up at least for 9. What do you think? Regards, Volker On Tue, Jun 9, 2015 at 1:35 AM, Mandy Chung <mandy.ch...@oracle.com> wrote: > Hi Volker, > > Your patch reminds me the question I have about loading zip library. > > In JDK 9 (and earlier release), zip library is loaded by the VM during > startup (see ClassLoader::load_zip_library). I think loadLibrary("zip") is > no longer needed to be called from System::initializeSystemClass method and > instead it can be loaded by java.util.zip.ZipFile static initializer. > > Do you mind to try the patch (below)? That may be a simpler fix. > > I never like the way jni_FindClass to look for the class context by calling > the NativeLibrary::getFromClass method. I will have to look deeper other > alternative to clean that up. If taking out loadLibrary("zip") resolves > your issue, this will give us time to come up with a better clean-up in the > future. > > Mandy > [1] https://bugs.openjdk.java.net/browse/JDK-4429040 > > diff --git a/src/share/classes/java/lang/System.java > b/src/share/classes/java/lang/System.java > --- a/src/share/classes/java/lang/System.java > +++ b/src/share/classes/java/lang/System.java > @@ -1192,10 +1192,6 @@ > setOut0(newPrintStream(fdOut, > props.getProperty("sun.stdout.encoding"))); > setErr0(newPrintStream(fdErr, > props.getProperty("sun.stderr.encoding"))); > > - // Load the zip library now in order to keep java.util.zip.ZipFile > - // from trying to use itself to load this library later. > - loadLibrary("zip"); > - > // Setup Java signal handlers for HUP, TERM, and INT (where > available). > Terminator.setup(); > > diff --git a/src/share/classes/java/util/zip/ZipFile.java > b/src/share/classes/java/util/zip/ZipFile.java > --- a/src/share/classes/java/util/zip/ZipFile.java > +++ b/src/share/classes/java/util/zip/ZipFile.java > @@ -83,6 +83,7 @@ > > static { > /* Zip library is loaded from System.initializeSystemClass */ > + System.loadLibrary("zip"); > initIDs(); > > } > > > > On 06/08/2015 07:23 AM, Volker Simonis wrote: >> >> Hi, >> >> can I please get a review at least for the part of this fix which is >> common for jdk8 and jdk9. It's only a few lines in >> src/share/vm/prims/jni.cpp for the hotspot repository and one line >> src/java.base/share/classes/java/lang/ClassLoader.java for the jdk >> repository. >> >> Thanks, >> Volker >> >> >> On Tue, Jun 2, 2015 at 6:12 PM, Volker Simonis <volker.simo...@gmail.com> >> wrote: >>> >>> Hi, >>> >>> can I please have review (and a sponsor) for these changes: >>> >>> https://bugs.openjdk.java.net/browse/JDK-8081674 >>> http://cr.openjdk.java.net/~simonis/webrevs/2015/8081674.jdk >>> http://cr.openjdk.java.net/~simonis/webrevs/2015/8081674.hs >>> >>> Please notice that the fix requires synchronous changes in the jdk and >>> the >>> hotspot forest. >>> >>> The changes themselves are by far not that big as this explanation but I >>> found the problem to be quite intricate so I tried to explain it as good >>> as >>> I could. I'd suggest to read the HTML-formatted explanation in the webrev >>> although the content is equal to the one in this mail: >>> >>> Using an unsupported character encoding (e.g. vi_VN.TCVN on Linux) >>> results >>> in an immediate VM failure with jdk 8 and 9: >>> >>> export LANG=vi_VN.TCVN >>> java -version >>> Error occurred during initialization of VM >>> java.util.EmptyStackException >>> at java.util.Stack.peek(Stack.java:102) >>> at >>> java.lang.ClassLoader$NativeLibrary.getFromClass(ClassLoader.java:1751) >>> at java.lang.ClassLoader$NativeLibrary.findBuiltinLib(Native >>> Method) >>> at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1862) >>> at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1835) >>> at java.lang.Runtime.loadLibrary0(Runtime.java:870) >>> at java.lang.System.loadLibrary(System.java:1119) >>> at java.lang.System.initializeSystemClass(System.java:1194) >>> >>> This is a consequence of "8005716: Enhance JNI specification to allow >>> support of static JNI libraries in Embedded JREs". >>> >>> With jdk 9 we get this error even if we're running with a supported >>> charset >>> which is in the ExtendedCharsets (as opposed to being in the >>> StandardCharsets) class which is a consequence of delegating the loading >>> of >>> the ExtendedCharsets class to the ServiceLoader in jdk 9. >>> >>> export LANG=eo.iso-8859-3 >>> output-jdk9/images/jdk/bin/java -version >>> Error occurred during initialization of VM >>> java.util.EmptyStackException >>> at java.util.Stack.peek(Stack.java:102) >>> at >>> java.lang.ClassLoader$NativeLibrary.getFromClass(ClassLoader.java:1737) >>> at java.lang.ClassLoader$NativeLibrary.findBuiltinLib(Native >>> Method) >>> at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1866) >>> at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1840) >>> at java.lang.Runtime.loadLibrary0(Runtime.java:874) >>> at java.lang.System.loadLibrary(System.java:1111) >>> at java.lang.System.initializeSystemClass(System.java:1186) >>> >>> Here's why the exception happens for an unsupported charset (see the >>> mixed >>> stack trace below for the full details): >>> >>> java.lang.System.loadLibrary() wants to load libzip.so. It calls >>> java.lang.Runtime.loadLibrary0() which at the very beginning calls the >>> native method ClassLoader$NativeLibrary.findBuiltinLib() which checks if >>> the >>> corresponding library is already statically linked into the VM >>> (introduced >>> by 8005716). >>> Java_java_lang_ClassLoader_00024NativeLibrary_findBuiltinLib(), >>> the native implementation of findBuiltinLib() in Classloader.c calls >>> GetStringPlatformChars() to convert the library name into the native >>> platform encoding. GetStringPlatformChars() calls the helper function >>> jnuEncodingSupported() to check if the platform encoding which is stored >>> in >>> the property "sun.jnu.encoding" is supported by Java. >>> jnuEncodingSupported() >>> is implemented as follows: >>> >>> static jboolean isJNUEncodingSupported = JNI_FALSE; >>> static jboolean jnuEncodingSupported(JNIEnv *env) { >>> jboolean exe; >>> if (isJNUEncodingSupported == JNI_TRUE) { >>> return JNI_TRUE; >>> } >>> isJNUEncodingSupported = (jboolean) JNU_CallStaticMethodByName ( >>> env, &exe, >>> "java/nio/charset/Charset", >>> "isSupported", >>> "(Ljava/lang/String;)Z", >>> jnuEncoding).z; >>> return isJNUEncodingSupported; >>> } >>> >>> Once the function finds that the platform encoding is supported (by >>> calling >>> java.nio.charset.Charset.isSupported()) it caches this value and always >>> returns it on following calls. However if the platform encoding is not >>> supported, it ALWAYS calls java.nio.charset.Charset.isSupported() an >>> every >>> subsequent invocation. >>> >>> In order to call the Java method Charset.isSupported() (in >>> JNU_CallStaticMethodByName() in file jni_util.c), we have to call >>> jni_FindClass() to convert the symbolic class name >>> "java.nio.charset.Charset" into a class reference. >>> >>> But unfortunately, jni_FindClass() (from jni.cpp in libjvm.so) has a >>> special >>> handling if called from java.lang.ClassLoader$NativeLibrary to ensure >>> that >>> JNI_OnLoad/JNI_OnUnload are executed in the correct class context: >>> >>> instanceKlassHandle k (THREAD, thread->security_get_caller_class(0)); >>> if (k.not_null()) { >>> loader = Handle(THREAD, k->class_loader()); >>> // Special handling to make sure JNI_OnLoad and JNI_OnUnload are >>> executed >>> // in the correct class context. >>> if (loader.is_null() && >>> k->name() == vmSymbols::java_lang_ClassLoader_NativeLibrary()) { >>> JavaValue result(T_OBJECT); >>> JavaCalls::call_static(&result, k, >>> vmSymbols::getFromClass_name(), >>> vmSymbols::void_class_signature(), >>> thread); >>> if (HAS_PENDING_EXCEPTION) { >>> Handle ex(thread, thread->pending_exception()); >>> CLEAR_PENDING_EXCEPTION; >>> THROW_HANDLE_0(ex); >>> } >>> >>> So if that's the case and jni_FindClass() was reallycalled from >>> ClassLoader$NativeLibrary, then jni_FindClass() calles >>> ClassLoader$NativeLibrary().getFromClass() to find out the corresponding >>> context class which is supposed to be saved there in a field of type >>> java.util.Stack named "nativeLibraryContext": >>> >>> // Invoked in the VM to determine the context class in >>> // JNI_Load/JNI_Unload >>> static Class<?> getFromClass() { >>> return ClassLoader.nativeLibraryContext.peek().fromClass; >>> } >>> >>> Unfortunately, "nativeLibraryContext" doesn't contain any entry at this >>> point and the invocation of Stack.peek() will throw the exception shown >>> before. In general, the "nativeLibraryContext" stack will be filled later >>> on >>> in Runtime.loadLibrary0() like this: >>> >>> NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin); >>> nativeLibraryContext.push(lib); >>> try { >>> lib.load(name, isBuiltin); >>> } finally { >>> nativeLibraryContext.pop(); >>> } >>> >>> such that it always contains at least one element later when >>> jni_FindClass() >>> will be invoked. >>> >>> So in summary, the problem is that the implementors of 8005716 didn't >>> took >>> into account that calling ClassLoader$NativeLibrary.findBuiltinLib() may >>> trigger a call to jni_FindClass() if we are running on a system with an >>> unsupported character encoding. >>> >>> I'd suggest the following fix for this problem: >>> >>> Change ClassLoader$NativeLibrary().getFromClass() to return null if the >>> stack is empty instead of throwing an exception: >>> >>> static Class<?> getFromClass() { >>> return ClassLoader.nativeLibraryContext.empty() ? >>> null : ClassLoader.nativeLibraryContext.peek().fromClass; >>> } >>> >>> Unfortunately this also requires a HotSpot change in jni_FindClass() in >>> order to properly handle the new 'null' return value: >>> >>> if (k.not_null()) { >>> loader = Handle(THREAD, k->class_loader()); >>> // Special handling to make sure JNI_OnLoad and JNI_OnUnload are >>> executed >>> // in the correct class context. >>> if (loader.is_null() && >>> k->name() == vmSymbols::java_lang_ClassLoader_NativeLibrary()) { >>> JavaValue result(T_OBJECT); >>> JavaCalls::call_static(&result, k, >>> vmSymbols::getFromClass_name(), >>> vmSymbols::void_class_signature(), >>> thread); >>> if (HAS_PENDING_EXCEPTION) { >>> Handle ex(thread, thread->pending_exception()); >>> CLEAR_PENDING_EXCEPTION; >>> THROW_HANDLE_0(ex); >>> } >>> oop mirror = (oop) result.get_jobject(); >>> if (oopDesc::is_null(mirror)) { >>> loader = Handle(THREAD, SystemDictionary::java_system_loader()); >>> } else { >>> loader = Handle(THREAD, >>> >>> InstanceKlass::cast(java_lang_Class::as_Klass(mirror))->class_loader()); >>> protection_domain = Handle(THREAD, >>> >>> >>> InstanceKlass::cast(java_lang_Class::as_Klass(mirror))->protection_domain()); >>> } >>> } >>> } else { >>> // We call ClassLoader.getSystemClassLoader to obtain the system >>> class >>> loader. >>> loader = Handle(THREAD, SystemDictionary::java_system_loader()); >>> } >>> >>> These changes are sufficient to solve the problem in Java 8. >>> Unfortunately, >>> that's still not enough in Java 9 because there the loading of the >>> extended >>> charsets has been delegated to ServiceLoader. But ServiceLoader calls >>> ClassLoader.getBootstrapResources() which calls >>> sun.misc.Launcher.getBootstrapClassPath(). This leads to another problem >>> during class initialization of sun.misc.Launcher if running on an >>> unsupported locale. >>> >>> The first thing done in sun.misc.Launcher.<clinit> is the initialisation >>> of >>> the bootstrap URLClassPath in the Launcher. However this initialisation >>> will >>> eventually call Charset.isSupported() and if we are running on an >>> unsupported locale this will inevitably end in another recursive call to >>> ServiceLoader. But as explained below, ServiceLoader will query the >>> Launcher's bootstrap URLClassPath which will be still uninitialized at >>> that >>> point. >>> >>> So we'll have to additionally guard guard against this situation on JDK 9 >>> like this: >>> >>> private static Charset lookupExtendedCharset(String charsetName) { >>> if (!sun.misc.VM.isBooted() || // see >>> lookupViaProviders() >>> sun.misc.Launcher.getBootstrapClassPath() == null) >>> return null; >>> >>> This fixes the crashes, but still at the price of not having the extended >>> charsets available during initialization until >>> Launcher.getBootstrapClassPath is set up properly. This may be still a >>> problem if the jdk is installed in a directory which contains characters >>> specific to an extended encoding or if we have such characters in the >>> command line arguments. >>> >>> Thank you and best regards, >>> Volker >>> >>> >>> Mixed stack trace of the initial EmptyStackException for unsupported >>> charsets described before: >>> >>> Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native >>> code) >>> j java.util.Stack.peek()Ljava/lang/Object;+1 >>> j java.lang.ClassLoader$NativeLibrary.getFromClass()Ljava/lang/Class;+3 >>> v ~StubRoutines::call_stub >>> V [libjvm.so+0x9d279a] JavaCalls::call_helper(JavaValue*, >>> methodHandle*, >>> JavaCallArguments*, Thread*)+0x6b4 >>> V [libjvm.so+0xcad591] os::os_exception_wrapper(void (*)(JavaValue*, >>> methodHandle*, JavaCallArguments*, Thread*), JavaValue*, methodHandle*, >>> JavaCallArguments*, Thread*)+0x45 >>> V [libjvm.so+0x9d20cf] JavaCalls::call(JavaValue*, methodHandle, >>> JavaCallArguments*, Thread*)+0x8b >>> V [libjvm.so+0x9d1d3b] JavaCalls::call_static(JavaValue*, KlassHandle, >>> Symbol*, Symbol*, JavaCallArguments*, Thread*)+0x139 >>> V [libjvm.so+0x9d1e3f] JavaCalls::call_static(JavaValue*, KlassHandle, >>> Symbol*, Symbol*, Thread*)+0x9d >>> V [libjvm.so+0x9e6588] jni_FindClass+0x428 >>> C [libjava.so+0x20208] JNU_CallStaticMethodByName+0xff >>> C [libjava.so+0x21cae] jnuEncodingSupported+0x61 >>> C [libjava.so+0x22125] JNU_GetStringPlatformChars+0x125 >>> C [libjava.so+0xedcd] >>> Java_java_lang_ClassLoader_00024NativeLibrary_findBuiltinLib+0x8b >>> j >>> >>> java.lang.ClassLoader$NativeLibrary.findBuiltinLib(Ljava/lang/String;)Ljava/lang/String;+0 >>> j java.lang.ClassLoader.loadLibrary0(Ljava/lang/Class;Ljava/io/File;)Z+4 >>> j >>> >>> java.lang.ClassLoader.loadLibrary(Ljava/lang/Class;Ljava/lang/String;Z)V+228 >>> j >>> java.lang.Runtime.loadLibrary0(Ljava/lang/Class;Ljava/lang/String;)V+54 >>> j java.lang.System.loadLibrary(Ljava/lang/String;)V+7 >>> j java.lang.System.initializeSystemClass()V+113 >>> v ~StubRoutines::call_stub >>> V [libjvm.so+0x9d279a] JavaCalls::call_helper(JavaValue*, >>> methodHandle*, >>> JavaCallArguments*, Thread*)+0x6b4 >>> V [libjvm.so+0xcad591] os::os_exception_wrapper(void (*)(JavaValue*, >>> methodHandle*, JavaCallArguments*, Thread*), JavaValue*, methodHandle*, >>> JavaCallArguments*, Thread*)+0x45 >>> V [libjvm.so+0x9d20cf] JavaCalls::call(JavaValue*, methodHandle, >>> JavaCallArguments*, Thread*)+0x8b >>> V [libjvm.so+0x9d1d3b] JavaCalls::call_static(JavaValue*, KlassHandle, >>> Symbol*, Symbol*, JavaCallArguments*, Thread*)+0x139 >>> V [libjvm.so+0x9d1e3f] JavaCalls::call_static(JavaValue*, KlassHandle, >>> Symbol*, Symbol*, Thread*)+0x9d >>> V [libjvm.so+0xe3cceb] call_initializeSystemClass(Thread*)+0xb0 >>> V [libjvm.so+0xe44444] >>> Threads::initialize_java_lang_classes(JavaThread*, >>> Thread*)+0x21a >>> V [libjvm.so+0xe44b12] Threads::create_vm(JavaVMInitArgs*, bool*)+0x4a6 >>> V [libjvm.so+0xa19bd7] JNI_CreateJavaVM+0xc7 >>> C [libjli.so+0xa520] InitializeJVM+0x154 >>> C [libjli.so+0x8024] JavaMain+0xcc >>> >>> >