diff --git a/docs/Java/Class Loader.md b/docs/Java/Class Loader.md index 58f74bf..0455552 100644 --- a/docs/Java/Class Loader.md +++ b/docs/Java/Class Loader.md @@ -22,6 +22,33 @@ nav_order: 2 ![img.png](img/class_load_extend.png) +```text +[arthas@56]$ classloader -t ++-BootstrapClassLoader ++-sun.misc.Launcher$ExtClassLoader@1806bc4c ++-com.taobao.arthas.agent.ArthasClassloader@70dbccd0 +| +-com.alibaba.arthas.deps.com.alibaba.fastjson.util.ASMClassLoader@65a9e522 +| +-com.alibaba.arthas.deps.com.alibaba.fastjson.util.ASMClassLoader@366d37d5 ++-sun.misc.Launcher$AppClassLoader@18b4aac2 ++-SandboxClassLoader[namespace=default;path=/opt/sandbox/lib/sandbox-core.jar;] ++-ModuleJarClassLoader[crc32=1008466651;file=/opt/sandbox/sandbox-module/repeater/repeater-module.jar;] +| +-com.alibaba.fastjson.util.ASMClassLoader@12a9ccaf +| +-com.alibaba.fastjson.util.ASMClassLoader@79f0e083 +| +-com.alibaba.jvm.sandbox.repeater.module.classloader.PluginClassLoader@7ac8c463 ++-ModuleJarClassLoader[crc32=705482150;file=/opt/sandbox/module/sandbox-mgr-module.jar;] ++-ModuleJarClassLoader[crc32=2819737704;file=/opt/sandbox/sandbox-module/tracking/tracking-module.jar;] +| +-com.frxs.foundation.tracking.module.classload.TrackingModuleClassLoader@7a356a0d +| +-com.alibaba.fastjson.util.ASMClassLoader@3bad566f +| +-com.frxs.foundation.tracking.module.classload.PluginClassLoader@38c9648e +| +-com.frxs.foundation.tracking.module.classload.PluginClassLoader@4d6f623d ++-com.alibaba.jvm.sandbox.core.classloader.ProviderClassLoader@524f3b3a ++-io.prometheus.jmx.shaded.net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader@4034c28c ++-org.springframework.boot.loader.LaunchedURLClassLoader@3289079a +| +-com.alibaba.fastjson.util.ASMClassLoader@53f86cc3 +| +-com.alibaba.fastjson.util.ASMClassLoader@19e2db7c +| +-java.net.URLClassLoader@6d4c18b8 +``` + ### 1.1.1. Bootstrap Class Loader 主要用来加载 JDK 内部的核心类库( %JAVA_HOME%/lib目录下的 rt.jar、resources.jar、charsets.jar等 jar 包和类)以及被 @@ -111,7 +138,7 @@ protected Class loadClass(String name, boolean resolve) 在RepeaterModule中调用上面的方法 -```text +```java ApplicationModel.instance().setConfig(config); // 特殊路由表; PluginClassLoader.Routing[] routingArray = PluginClassRouting.wellKnownRouting(configInfo.getMode() == Mode.AGENT, 20L); @@ -129,7 +156,7 @@ ApplicationModel.instance().setConfig(config); ![img.png](img/class_loader_jdbc_driver.png) -``` +```java java.security.AccessController#doPrivileged(java.security.PrivilegedAction) static { loadInitialDrivers(); @@ -187,9 +214,142 @@ while (isPreloading && --timeout > 0 && ClassloaderBridge.instance().findClassIn 启动类加载器,优先级最高,即BootstrapClassLoader +### 类的桥接 + +```mermaid +classDiagram + class AutoCloseable { + <> + } + class ClassLoader { + - Map~String, Boolean~ packageAssertionStatus + - Vector~Class~?~~ classes + - ProtectionDomain defaultDomain + - HashMap~String, Package~ packages + - boolean defaultAssertionStatus + - Certificate[] nocerts + - ConcurrentHashMap~String, Object~ parallelLockMap + - Vector~String~ loadedLibraryNames + - boolean sclSet + - Vector~NativeLibrary~ systemNativeLibraries + - Map~String, Certificate[]~ package2certs + - Stack~NativeLibrary~ nativeLibraryContext + ~ Object assertionLock + - String[] sys_paths + - Vector~NativeLibrary~ nativeLibraries + - ClassLoader scl + - String[] usr_paths + ~ Map~String, Boolean~ classAssertionStatus + - ClassLoader parent + } + class Closeable { + <> + } + class ModuleJarClassLoader { + - long checksumCRC32 + - Logger logger + - File moduleJarFile + - File tempModuleJarFile + } + class RoutingURLClassLoader { + - Logger logger + - ClassLoadingLock classLoadingLock + - Routing[] routingArray + } + class SecureClassLoader { + - HashMap~CodeSource, ProtectionDomain~ pdcache + - Debug debug + - boolean initialized + } + class Stealth + class URLClassLoader { + - URLClassPath ucp + - WeakHashMap~Closeable, Void~ closeables + - AccessControlContext acc + } + + class DelegateBizClassLoader { + super(TomcatEmbeddedWebappClassLoader) + super.loadClass() + } + + class TomcatEmbeddedWebappClassLoader { + loadClass() + } + + Closeable --> AutoCloseable + ModuleJarClassLoader --> RoutingURLClassLoader :***** + Stealth .. ModuleJarClassLoader + RoutingURLClassLoader .. DelegateBizClassLoader :***** + RoutingURLClassLoader --> URLClassLoader + TomcatEmbeddedWebappClassLoader --> ClassLoader :***** + DelegateBizClassLoader --> TomcatEmbeddedWebappClassLoader :***** + SecureClassLoader --> ClassLoader + DelegateBizClassLoader --> ClassLoader + URLClassLoader ..> Closeable + URLClassLoader "1" *--> "closeables *" Closeable + URLClassLoader --> SecureClassLoader + +``` + +在jvm-sandbox中,希望在沙箱中的代码可以直接调用业务代码中的类。但双方是不同的类 +加载器,所以在沙箱加载中,可以使用业务代码的类代码器,如Spring的LauncherURLClassLoader去 +加载类,如上图中,****线的关联关系。 + +- DelegateBizClassLoader delegateBizClassLoader = BusinessClassLoaderHolder.getBussinessClassLoader(); + +下面的代码中, +```java +@Override + protected Class loadClass(final String javaClassName, final boolean resolve) throws ClassNotFoundException { + synchronized (getClassLoadingLock0(javaClassName)){ + // 优先查询类加载路由表,如果命中路由规则,则优先从路由表中的ClassLoader完成类加载 + if (ArrayUtils.isNotEmpty(routingArray)) { + for (final Routing routing : routingArray) { + if (!routing.isHit(javaClassName)) { + continue; + } + final ClassLoader routingClassLoader = routing.classLoader; + try { + return routingClassLoader.loadClass(javaClassName); + } catch (Exception cause) { + // 如果在当前routingClassLoader中找不到应该优先加载的类(应该不可能,但不排除有就是故意命名成同名类) + // 此时应该忽略异常,继续往下加载 + // ignore... + } + } + } + + // 先走一次已加载类的缓存,如果没有命中,则继续往下加载 + final Class loadedClass = findLoadedClass(javaClassName); + if (loadedClass != null) { + return loadedClass; + } + + try { + Class aClass = findClass(javaClassName); + if (resolve) { + resolveClass(aClass); + } + return aClass; + } catch (Exception cause) { + DelegateBizClassLoader delegateBizClassLoader = BusinessClassLoaderHolder.getBussinessClassLoader(); + try { + if(null != delegateBizClassLoader){ + return delegateBizClassLoader.loadClass(javaClassName,resolve); + } + } catch (Exception e) { + //忽略异常,继续往下加载 + } + return RoutingURLClassLoader.super.loadClass(javaClassName, resolve); + } + } + } +``` + # jdk9的类加载器 - 启动类加载器,使用java编码,在jdk. -- + # 1.5. 参考文献 diff --git "a/docs/Sandbox/jvm-sadnbox-1.4.0\347\211\210\346\234\254\344\270\215\346\224\257\346\214\201\345\242\236\345\274\272\344\270\232\345\212\241\346\250\241\345\235\227\347\232\204\347\261\273.md" "b/docs/Sandbox/jvm-sadnbox-1.4.0\347\211\210\346\234\254\344\270\215\346\224\257\346\214\201\345\242\236\345\274\272\344\270\232\345\212\241\346\250\241\345\235\227\347\232\204\347\261\273.md" new file mode 100644 index 0000000..d969785 --- /dev/null +++ "b/docs/Sandbox/jvm-sadnbox-1.4.0\347\211\210\346\234\254\344\270\215\346\224\257\346\214\201\345\242\236\345\274\272\344\270\232\345\212\241\346\250\241\345\235\227\347\232\204\347\261\273.md" @@ -0,0 +1,124 @@ +--- +layout: default +title: jvm-sadnbox-1.4.0版本不支持增强业务模块的类 +parent: Sandbox +--- + + +# 原因分析 + +## 分析1 +对比了1.4.0和1.3.0的代码,有以下代码,看着像是把业务类与sandbox类分离,后来发现在搞错了,这个是为了增强一类native本地方法的 +```java +// 如果未开启unsafe开关,是不允许增强来自BootStrapClassLoader的类 +if (!isEnableUnsafe + && null == loader) { +logger.debug("transform ignore {}, class from bootstrap but unsafe.enable=false.", internalClassName); + return null; +} +``` + +## 分析2 +对比了1.4.0和1.3.0的代码,发现BusinessClassLoaderHolder类被移除了,因为每次增强被触发后,是以事件监听器来处理的,所以1.3.3版本是 +在EventListenerHandler类中以如下代码。这串代码在1.4.0中移除了。所以在EventListenerHandler(这个类处在sandbox.core包下)执行时, +线程的上下文不是业务类加载器,是jvm-sandbox的类加载器。 +```java +package com.alibaba.jvm.sandbox.core.classloader; + +/** + * @author zhuangpeng + * @since 2020/1/15 + */ +public class BusinessClassLoaderHolder { + + private static final ThreadLocal holder = new ThreadLocal(); + + public static void setBussinessClassLoader(ClassLoader classLoader){ + if(null == classLoader){ + return; + } + DelegateBizClassLoader delegateBizClassLoader = new DelegateBizClassLoader(classLoader); + holder.set(delegateBizClassLoader); + } + + + public static void removeBussinessClassLoader(){ + holder.remove(); + } + + public static DelegateBizClassLoader getBussinessClassLoader(){ + + return null != holder ? holder.get() : null; + } + + public static class DelegateBizClassLoader extends ClassLoader{ + public DelegateBizClassLoader(ClassLoader parent){ + super(parent); + } + + @Override + public Class loadClass(final String javaClassName, final boolean resolve) throws ClassNotFoundException { + return super.loadClass(javaClassName,resolve); + } + } +} + +``` +在类加载中,在事件处置中,将参数的类加载器,置入,代码如下: + +```sql +final ClassLoader javaClassLoader = ObjectIDs.instance.getObject(targetClassLoaderObjectID); + //放置业务类加载器 + BusinessClassLoaderHolder.setBussinessClassLoader(javaClassLoader); + +``` +这个加载器使用,类加载的时机 +```java +@Override + protected Class loadClass(final String javaClassName, final boolean resolve) throws ClassNotFoundException { + synchronized (getClassLoadingLock0(javaClassName)){ + // 优先查询类加载路由表,如果命中路由规则,则优先从路由表中的ClassLoader完成类加载 + if (ArrayUtils.isNotEmpty(routingArray)) { + for (final Routing routing : routingArray) { + if (!routing.isHit(javaClassName)) { + continue; + } + final ClassLoader routingClassLoader = routing.classLoader; + try { + return routingClassLoader.loadClass(javaClassName); + } catch (Exception cause) { + // 如果在当前routingClassLoader中找不到应该优先加载的类(应该不可能,但不排除有就是故意命名成同名类) + // 此时应该忽略异常,继续往下加载 + // ignore... + } + } + } + + // 先走一次已加载类的缓存,如果没有命中,则继续往下加载 + final Class loadedClass = findLoadedClass(javaClassName); + if (loadedClass != null) { + return loadedClass; + } + + try { + Class aClass = findClass(javaClassName); + if (resolve) { + resolveClass(aClass); + } + return aClass; + } catch (Exception cause) { + DelegateBizClassLoader delegateBizClassLoader = BusinessClassLoaderHolder.getBussinessClassLoader(); + try { + if(null != delegateBizClassLoader){ + return delegateBizClassLoader.loadClass(javaClassName,resolve); + } + } catch (Exception e) { + //忽略异常,继续往下加载 + } + return RoutingURLClassLoader.super.loadClass(javaClassName, resolve); + } + } + } + +``` +问题可以定位是因为分析2。 \ No newline at end of file