Deep JVM Analysis Spring-boot application Hibernate-validator

Source: Internet
Author: User
Tags throwable jboss

Problem
    • Reproducible Demo Code: Demo.zip

Recently troubleshooting a spring boot application that throws Hibernate.validator Noclassdeffounderror, the exception information is as follows:

caused by:java.lang.NoClassDefFoundError:Could not initialize class Org.hibernate.validator.internal.engine.ConfigurationImpl at Org.hibernate.validator.HibernateValidator.createGenericConfiguration (hibernatevalidator.java:33) ~[ Hibernate-validator-5.3.5.final.jar:5.3.5.final] at Javax.validation.validation$genericbootstrapimpl.configure ( validation.java:276) ~[validation-api-1.1.0.final.jar:na] at Org.springframework.boot.validation.MessageInterpolatorFactory.getObject (messageinterpolatorfactory.java:53) ~[ Spring-boot-1.5.3.release.jar:1.5.3.release] At Org.springframework.boot.autoconfigure.validation.DefaultValidatorConfiguration.defaultValidator ( defaultvalidatorconfiguration.java:43) ~[spring-boot-autoconfigure-1.5.3.release.jar:1.5.3.release] at SUN.REFLECT.NATIVEMETHODACCESSORIMPL.INVOKE0 (Native Method) ~[na:1.8.0_112] at Sun.reflect.NativeMethodAccessorImpl.invoke (nativemethodaccessorimpl.java:62) ~[na:1.8.0_112] at Sun.reflect.DelegatingMethodAccessorImpl.Invoke (delegatingmethodaccessorimpl.java:43) ~[na:1.8.0_112] at Java.lang.reflect.Method.invoke (method.java:498) ~ [na:1.8.0_112] at Org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate ( simpleinstantiationstrategy.java:162) ~[spring-beans-4.3.8.release.jar:4.3.8.release] ... Common frames omitted

This error message is ostensibly NoClassDefFoundError , but in fact the ConfigurationImpl class is in hibernate-validator-5.3.5.Final.jar , and there should not be a case where the class cannot be found.

So why did you throw this in the app NoClassDefFoundError ?

An experienced developer Could not initialize class can tell from this information that it is actually an exception thrown by a class at initialization, such as static code blocks of static, or exceptions that are initialized by a static field.

who initialized the Org.hibernate.validator.internal.engine.ConfigurationImpl

But when we HibernateValidator break points in this class, created ConfigurationImpl code block, we find that there are two threads triggering breakpoints:

 Public class Implements Validationprovider {    @Override    public Configuration<?> creategenericconfiguration (bootstrapstate state) {        returnNew  Configurationimpl (state);    }

The call stack for one of the threads is:

Thread [Background-preinit] (Class load:configurationimpl)    hibernatevalidator.creategenericconfiguration ( bootstrapstate) line:33    validation$genericbootstrapimpl.configure () line:276    backgroundpreinitializer$ Validationinitializer.run () line:107    backgroundpreinitializer$1.runsafely (Runnable) line:59    Backgroundpreinitializer$1.run () line:52    thread.run () line:745

Another thread call stack is:

Thread [main] (Suspended (breakpoint at line hibernatevalidator)) owns:concurrenthashmap<k,v> (id=52) Owns:object (id=53) hibernatevalidator.creategenericconfiguration (bootstrapstate) line:33 Validation$GenericBoots Trapimpl.configure () line:276 messageinterpolatorfactory.getobject () line:53 Defaultvalidatorconfiguration.default    Validator () line:43 nativemethodaccessorimpl.invoke0 (Method, Object, object[]) Line:not available [native method] Nativemethodaccessorimpl.invoke (object, object[]) line:62 Delegatingmethodaccessorimpl.invoke (object, Object[]) Line:43 Method.invoke (Object, Object ...) line:498 cglibsubclassinginstantiationstrategy (Simpleinstantiationstrateg y). Instantiate (Rootbeandefinition, String, Beanfactory, Object, Method, Object ...) line:162 Constructorresolver.insta Ntiateusingfactorymethod (String, Rootbeandefinition, object[]) line:588 defaultlistablebeanfactory ( abstractautowirecapablebeanfactory). InstantiateusiNgfactorymethod (String, Rootbeandefinition, object[]) line:1173 

Obviously, the call stack for this thread is a common spring initialization process.

What did Backgroundpreinitializer do?

So let's focus on BackgroundPreinitializer what the thread does:

@Order (Loggingapplicationlistener.default_order + 1) Public classBackgroundpreinitializerImplementsApplicationlistener<applicationenvironmentpreparedevent>{@Override Public voidonapplicationevent (Applicationenvironmentpreparedevent event) {Try{thread thread=NewThread (NewRunnable () {@Override Public voidrun () {runsafely (NewMessageconverterinitializer ()); Runsafely (NewMbeanfactoryinitializer ()); Runsafely (NewValidationinitializer ()); Runsafely (NewJacksoninitializer ()); Runsafely (NewConversionserviceinitializer ()); }                 Public voidrunsafely (Runnable Runnable) {Try{runnable.run (); }                    Catch(Throwable ex) {//Ignore                    }                }            }, "Background-preinit");        Thread.Start (); }

You can see that the BackgroundPreinitializer class is spring boot in order to accelerate the initialization of the application, to load hibernate validator These components with a separate thread.

This background-preinit thread will swallow all the exceptions.

Obviously the ConfigurationImpl initialization of the exception is also swallowed, then how to get the most primitive information?

Gets the most primitive exception information

In BackgroundPreinitializer the run() function of a breakpoint (note is Suspend thread type, not Suspend VM ), let it do not trigger ConfigurationImpl the load, let spring boot normal process to trigger ConfigurationImpl the load, you can know the specific information.

The exception message that is called out is:

caused By:java.lang.NoSuchMethodError:org.jboss.logging.Logger.getMessageLogger (ljava/lang/class;    ljava/lang/string;) Ljava/lang/object; At Org.hibernate.validator.internal.util.logging.LoggerFactory.make (loggerfactory.java:19) ~[ Hibernate-validator-5.3.5.final.jar:5.3.5.final] at org.hibernate.validator.internal.util.version.<clinit> (version.java:22) ~[hibernate-validator-5.3.5.final.jar:5.3.5.final] at Org.hibernate.validator.internal.engine.configurationimpl.<clinit> (configurationimpl.java:71) ~[ Hibernate-validator-5.3.5.final.jar:5.3.5.final] At Org.hibernate.validator.HibernateValidator.createGenericConfiguration (hibernatevalidator.java:33) ~[ Hibernate-validator-5.3.5.final.jar:5.3.5.final] at Javax.validation.validation$genericbootstrapimpl.configure ( validation.java:276) ~[validation-api-1.1.0.final.jar:na] at Org.springframework.boot.validation.MessageInterpolatorFactory.getObject (messageinterpolatorfactory.java:53) ~[ Spring-boot-1.5.3.release.jar:1.5.3.RELEASE] 

It can be seen that org.jboss.logging.Logger this class is incompatible and less of getMessageLogger(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Object this function.

Then check the dependency of the application, can be found org.jboss.logging.Logger in jboss-common-1.2.1.GA.jar and in jboss-logging-3.3.1.Final.jar both.

Obviously jboss-common-1.2.1.GA.jar this dependency is outdated and needs to be ruled out.

Summarize the occurrence process of the exception
    1. Application dependent jboss-common-1.2.1.GA.jar , it's org.jboss.logging.Logger too old inside
    2. When spring boot starts, the BackgroundPreinitializer threads in the thread go to try to load and ConfigurationImpl then trigger org.jboss.logging.Logger the function execution problem
    3. BackgroundPreinitializerAte the exception information and the JVM ConfigurationImpl marked it as unavailable.
    4. Spring boot normal process to load ConfigurationImpl , JVM Discovery ConfigurationImpl class is not available, throw directlyNoClassDefFoundError

      "'
      caused by:Java.lang.NoClassDefFoundError:Could not initialize class Org.hibernate.validator.internal.engine.ConfigurationImpl
      ““

Deep JVM

Why does the second attempt to load ConfigurationImpl be thrown directly java.lang.NoClassDefFoundError: Could not initialize class ?

Here's a simple code to reproduce the problem:

Try {  catch  (Throwable e) {  e.printstacktrace ();} System.in.read (); Try {  catch  (Throwable e) {  e.printstacktrace ();}
Use HSDB to determine the state of a class

When the first exception is thrown, try to look at the state of the class with Hsdb.

sudo " $JAVA _home/lib/sa-jdi.jar " Sun.jvm.hotspot.HSDB

And then find Version the address information in the HSDB console.

Hsdb> class org.hibernate.validator.internal.util.Versionorg/hibernate/validator/internal/util/ Version @0x00000007c0060218

And then I found Inspector this address and found _init_state it to be 5.

Looking at the hotspot code, you can see that 5 corresponds to the definition initialization_error :

/hotspot/src/share/vm/oops/instanceklass.hppSee"The Java Virtual Machine Specification"Section 2.16.2-5 fora detailed descriptionThe Class loading &initialization procedure, and the use of the States.enum classstate {allocated,//allocated (but not yet linked) loaded,Loaded and insertedinchclass hierarchy (but not linked yet) linked,Successfully linked/verified (but not initialized yet) being_initialized,//currently running class initializer fully_initialized,//initialized (successfull final state) Initialization_error//error happened during initialization};
Content about initialization in the JVM specification

http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.5

From the specification you can see that the initial class/interface has 12 steps, the more important two steps are marked in bold:

      1. If the Class object for a C is in a erroneous state and then initialization are not possible. Release LC and throw a noclassdeffounderror.
      1. Otherwise, the class or interface initialization method must has completed abruptly by throwing some exception E. If the class of E is not an Error or one of its subclasses and then create a new instance of the class Exceptionininitializererr Or with an e as the argument, and use this object on place of E in the following step.
The first time you try to load the version class

When the first attempt is loaded, the hotspot Interpreterruntime attempts to load the class when parsing the invokestatic instruction, org.hibernate.validator.internal.util.Version InstanceKlass _init_state first marked as being_initialized , and then marked as failed when the load fails initialization_error .

Initializationthe corresponding 11 steps.

hotspot/src/share/vm/oops/instanceklass.cpp//Step and 11Handle E (THREAD, pending_exception);  clear_pending_exception;//JVMTI has already reported the PENDING exception//JVMTI internal flag reset was needed in order To report Exceptionininitializererrorjvmtiexport::clear_detected_exception ((javathread*) THREAD);  {Exception_mark;  This_oop->set_initialization_state_and_notify (Initialization_error, THREAD);   Clear_pending_exception; Ignore any exception thrown, class initialization error was thrown below//JVMTI has already reported the pending EXCE Ption//JVMTI internal flag Reset is needed on order to report Exceptionininitializererror Jvmtiexport::clear_detected_ Exception ((javathread*) THREAD);} Dtrace_classinit_probe_wait (Error, Instanceklass::cast (This_oop ()), -1,wait), if (e->is_a::  Error_klass ())) {Throw_oop (E ()));} else {javacallarguments args (e); Throw_arg (Vmsymbols::java_lang_exceptionininitializererror (), Vmsymbols::throwable_voId_signature (), &args);} 
The second time you try to load the version class

When you try to load the second time, the check InstanceKlass _init_state is initialization_error thrown directly NoClassDefFoundError: Could not initialize class .

Initializationthe corresponding 5 steps.

Hotspot/src/share/vm/oops/instanceklass.cppvoid Instanceklass::initialize_impl (InstanceKlassHandle this_oop, TRAPS) {//...    Step 5    if (This_oop->is_in_error_state ()) {      dtrace_classinit_probe_wait (erroneous, Instanceklass:: Cast (This_oop ()), -1,wait);      Resourcemark rm (THREAD);      CONST char* desc = "Could not initialize class";      Const char* ClassName = This_oop->external_name ();      size_t Msglen = strlen (desc) + strlen (className) + 1;      char* message = New_resource_array (char, msglen);      if (NULL = = message) {        //out of Memory:can ' t create detailed error message        throw_msg (VMSYMBOLS::JAVA_LANG_NOCLA Ssdeffounderror (), className);      } else {        jio_snprintf (message, Msglen, "%s%s", Desc, className);        Throw_msg (Vmsymbols::java_lang_noclassdeffounderror (), message);      }    }
Summarize
    • Spring Boot BackgroundPreinitializer uses a separate thread in the class to load the validator and eats the original exception
    • The first time the failed class is loaded, it will be marked in the JVM initialization_error and will be thrown directly when it is loaded again.NoClassDefFoundError: Could not initialize class
    • Be cautious when swallowing exceptions in your code, or you'll have great difficulty troubleshooting.
    • http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.5

http://blog.csdn.net/hengyunabc/article/details/71513509

Deep JVM Analysis Spring-boot application Hibernate-validator

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.