Cabbage Java study room covers core knowledge

Tomcat lightweight application server principle probe secret architecture analysis part Tomcat lightweight application server principle probe secret one key stop start part Tomcat lightweight application server principle probe secret class loader part Tomcat lightweight application server principle probe secret asynchronous communication part

1. Class loading mechanism

What is Java’s classloading mechanism?

The.java file is compiled, read into the Java method area, and the class loader loads, validates (class-specific format), prepares (allocates memory), resolves (changes bytecode symbolic references to direct references), and initializes (object initialization).

Please refer to the author’s article: The Java Engineer’s Path to the JVM

2. Parent delegation mechanism

What is the parent delegate mechanism?

When a classloader needs to load a.class file, it first delegates the task to its parent classloader, recurses the operation, and only loads the class itself if the parent classloader does not.

BootstrapClassLoader

Write c++, load Java core library Java.*, construct ExtClassLoader and AppClassLoader. Because the bootstrap class loader involves the local implementation details of the virtual machine, the developer cannot directly obtain the reference to the bootstrap class loader, so it is not allowed to operate directly by reference.

ExtClassLoader (standard extended class loader)

Written in Java to load extension libraries, such as jre in classpath, javax.*, or classes in the java.ext.dir specified location, developers can directly use the standard extension class loader.

AppClassLoader (system class loader)

Written in Java, the directory where the loader resides.

CustomClassLoader (CustomClassLoader)

Java written, user-defined class loader, can load the specified path of the class file.

The role of the parent delegation mechanism

  1. To prevent the repeated loading of the same.class, after loading, do not need to load again, to ensure data security.
  2. This ensures that the core classes provided by the JVM are not tampered with, ensuring class execution security.

3. Java 8 ClassLoad source code

If a developer needs a custom loader, the loadClass() and findClass() methods below need to be rewritten with renewed attention, thus breaking the parent delegation mechanism.

The loadClass() method in ClassLoad

protected Class<? > loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<? > c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent ! = null) {// is a recursive call until there are no parent class loaders c = parent-loadclass (name, false); } else {// Java core class library loader c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); C = findClass(name); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; }}Copy the code

The findClass() method in URLClassloader

protected Class<? > findClass(final String name) throws ClassNotFoundException { final Class<? > result; try { result = AccessController.doPrivileged( new PrivilegedExceptionAction<Class<? >>() { public Class<? > run() throws ClassNotFoundException {// Concatenates the. Class file with the specified prefix String Path = name.replace('.', '/').concat(".class"); // Obtain the. Class file from classpath. Resource res = UCp.getResource (path, false); if (res ! = null) {try {// read the class byte stream from the path and define it as a class; todo bytebuffer return defineClass(name, res); } catch (IOException e) { throw new ClassNotFoundException(name, e); } } else { return null; } } }, acc); } catch (java.security.PrivilegedActionException pae) { throw (ClassNotFoundException) pae.getException(); } if (result == null) { throw new ClassNotFoundException(name); } return result; }Copy the code

4. How does Tomcat break parent delegation

The following is a schematic diagram of the class loader as shown in the Official Tomcat documentation:

      Bootstrap
          |
       System
          |
       Common
       /     \
  Webapp1   Webapp2 ..
Copy the code
  • Bootstrap: Java’s highest loader, implemented in C language, is mainly used to load core classes required for JVM startup, such as $JAVA_HOME/jre/lib/ext path classes.
  • System: loads all classes in the path defined by the CLASSPATH System variable.
  • Common: loads all classes in the lib file in the Tomcat path.
  • Webapp1, Webapp2: loads all classes in the project in the WebApp path. There is a WebappClassLoader for each project, thus achieving class isolation between applications.

So why does Tomcat need a custom classloader?

  • Performance: If multiple applications are deployed in the same Tomcat, they all have the same library dependencies. Then the same class library can be loaded by the Common class loader.
  • Flexibility: Web applications are independent of each other’s classloaders, so it is possible to replace the original classloaders by reconstructing different files without affecting other applications.
  • Application isolation: Different applications A and B deployed in the same Tomcat, such as A using Spring 2.5 and B using Spring 3.5, use the same class loader and the Web application will fail to start due to the JAR package.

Tomcat breaks the parent delegation mechanism

Tomcat customizes the WebAppClassLoader class loader, breaking the parent delegation mechanism. In other words, if the application receives a class loading request, it will try to load the class by itself. If it cannot find the class loading request, it will send the class to the parent loader to load the class. The purpose is to load the class defined by the Web application first.

Tomcat overrides the loadClass() and findClass() methods below to break the parent delegation mechanism.

The loadClass() method in ClassLoad

@Override public Class<? > loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<? > clazz = null; Clazz = findLoadedClass0(name); if (clazz ! = null) { if (log.isDebugEnabled()) log.debug(" Returning class from cache"); if (resolve) resolveClass(clazz); return clazz; Clazz = findLoadedClass(name); // 2. if (clazz ! = null) { if (log.isDebugEnabled()) log.debug(" Returning class from cache"); if (resolve) resolveClass(clazz); return clazz; } String resourceName = binaryNameToPath(name, false); // 3. Try to load the class using the ExtClassLoader ClassLoader to prevent the Web application from overwriting the core class of the JRE ClassLoader javaseLoader = getJavaseClassLoader(); boolean tryLoadingFromJavaseLoader; try { URL url; if (securityManager ! = null) { PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName); url = AccessController.doPrivileged(dp); } else { url = javaseLoader.getResource(resourceName); } tryLoadingFromJavaseLoader = (url ! = null); } catch (Throwable t) { tryLoadingFromJavaseLoader = true; } boolean delegateLoad = delegate || filter(name, true); If (delegateLoad) {if (delegateLoad) {if (log.isdebugenabLED ()) log.debug(" Delegating to ") parent classloader1 " + parent); try { clazz = Class.forName(name, false, parent); if (clazz ! = null) { if (log.isDebugEnabled()) log.debug(" Loading class from parent"); if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } } // 5. If (log.isdebugenabled ()) log.debug(" Searching local Repositories "); try { clazz = findClass(name); if (clazz ! = null) { if (log.isDebugEnabled()) log.debug(" Loading class from local repository"); if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } // 6. If no class is found in WebAppClassLoader, delegate to AppClassLoader to load if (! delegateLoad) { if (log.isDebugEnabled()) log.debug(" Delegating to parent classloader at end: " + parent); try { clazz = Class.forName(name, false, parent); if (clazz ! = null) { if (log.isDebugEnabled()) log.debug(" Loading class from parent"); if (resolve) resolveClass(clazz); return clazz; } } catch (ClassNotFoundException e) { // Ignore } } } throw new ClassNotFoundException(name); }Copy the code

The findClass() method in URLClassloader

@Override public Class<? > findClass(String name) throws ClassNotFoundException { if (log.isDebugEnabled()) log.debug(" findClass(" + name + ") "); checkStateForClassLoading(name); // (1) Permission to define this class when using a SecurityManager if (securityManager ! = null) { int i = name.lastIndexOf('.'); if (i >= 0) { try { if (log.isTraceEnabled()) log.trace(" securityManager.checkPackageDefinition"); securityManager.checkPackageDefinition(name.substring(0,i)); } catch (Exception se) { if (log.isTraceEnabled()) log.trace(" -->Exception-->ClassNotFoundException", se); throw new ClassNotFoundException(name, se); } } } // Ask our superclass to locate this class, if possible // (throws ClassNotFoundException if it is not found) Class<? > clazz = null; try { if (log.isTraceEnabled()) log.trace(" findClassInternal(" + name + ")"); try { if (securityManager ! = null) { PrivilegedAction<Class<? >> dp = new PrivilegedFindClassByName(name); clazz = AccessController.doPrivileged(dp); } else {// find the internal mode from the local concrete class name clazz = findClassInternal(name); } } catch(AccessControlException ace) { log.warn("WebappClassLoader.findClassInternal(" + name + ") security exception: " + ace.getMessage(), ace); throw new ClassNotFoundException(name, ace); } catch (RuntimeException e) { if (log.isTraceEnabled()) log.trace(" -->RuntimeException Rethrown", e); throw e; } if ((clazz == null) && hasExternalRepositories) {try {clazz = super.findClass(name); } catch(AccessControlException ace) { log.warn("WebappClassLoader.findClassInternal(" + name + ") security exception: " + ace.getMessage(), ace); throw new ClassNotFoundException(name, ace); } catch (RuntimeException e) { if (log.isTraceEnabled()) log.trace(" -->RuntimeException Rethrown", e); throw e; } } if (clazz == null) { if (log.isDebugEnabled()) log.debug(" --> Returning ClassNotFoundException"); throw new ClassNotFoundException(name); } } catch (ClassNotFoundException e) { if (log.isTraceEnabled()) log.trace(" --> Passing on ClassNotFoundException"); throw e; } // Return the class we have located if (log.isTraceEnabled()) log.debug(" Returning class " + clazz); if (log.isTraceEnabled()) { ClassLoader cl; if (Globals.IS_SECURITY_ENABLED){ cl = AccessController.doPrivileged( new PrivilegedGetClassLoader(clazz)); } else {// If the parent class can't be loaded again cl = clazz.getClassLoader(); } log.debug(" Loaded by " + cl.toString()); } return clazz; }Copy the code

The default class loading order for Web applications (breaking the parent delegate rule) is:

  1. First load it from the JVM’s BootStrapClassLoader.
  2. Load the classes in /WEB-INF/classes of the Web application.
  3. Load the classes in the jar package in/web-INF /lib/*. Jap of the Web application.
  4. Load the classes under the System path defined above.
  5. Load the classes under the Common path defined above.

If ‘ ‘is configured in the configuration file, then the parent delegate rule is followed and the load order is as follows:

  1. First load it from the JVM’s BootStrapClassLoader.
  2. Load the classes under the System path defined above.
  3. Load the classes under the Common path defined above.
  4. Load the classes in /WEB-INF/classes of the Web application.
  5. Load the classes in the jar package in/web-INF /lib/*. Jap of the Web application.

Tomcat lightweight application server principle probe secret architecture analysis part Tomcat lightweight application server principle probe secret one key stop start part Tomcat lightweight application server principle probe secret class loader part Tomcat lightweight application server principle probe secret asynchronous communication part