diff --git a/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java b/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java index 0f34ed03..44781a18 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java +++ b/core/src/main/java/com/taobao/arthas/core/command/klass100/ClassLoaderCommand.java @@ -13,6 +13,7 @@ import com.taobao.arthas.core.shell.command.AnnotatedCommand; import com.taobao.arthas.core.shell.command.CommandProcess; import com.taobao.arthas.core.shell.handlers.Handler; import com.taobao.arthas.core.util.ClassUtils; +import com.taobao.arthas.core.util.ClassLoaderUtils; import com.taobao.arthas.core.util.ResultUtils; import com.taobao.arthas.core.util.affect.RowAffect; import com.taobao.middleware.cli.annotations.Description; @@ -56,6 +57,7 @@ public class ClassLoaderCommand extends AnnotatedCommand { private Logger logger = LoggerFactory.getLogger(ClassLoaderCommand.class); private boolean isTree = false; private String hashCode; + private String classLoaderClass; private boolean all = false; private String resource; private boolean includeReflectionClassLoader = true; @@ -70,6 +72,12 @@ public class ClassLoaderCommand extends AnnotatedCommand { public void setTree(boolean tree) { isTree = tree; } + + @Option(longName = "classLoaderClass") + @Description("The class name of the special class's classLoader.") + public void setClassLoaderClass(String classLoaderClass) { + this.classLoaderClass = classLoaderClass; + } @Option(shortName = "c", longName = "classloader") @Description("The hash code of the special ClassLoader") @@ -113,6 +121,25 @@ public class ClassLoaderCommand extends AnnotatedCommand { process.interruptHandler(new ClassLoaderInterruptHandler(this)); Instrumentation inst = process.session().getInstrumentation(); + + if (!all && hashCode == null && classLoaderClass != null) { + List matchedClassLoaders = ClassLoaderUtils.getClassLoaderByClassName(inst, classLoaderClass); + if (matchedClassLoaders.size() == 1) { + hashCode = "" + Integer.toHexString(matchedClassLoaders.get(0).hashCode()); + } else if (matchedClassLoaders.size() > 1) { + Collection classLoaderVOList = ClassUtils.createClassLoaderVOList(matchedClassLoaders); + ClassLoaderModel classloaderModel = new ClassLoaderModel() + .setClassLoaderClass(classLoaderClass) + .setMatchedClassLoaders(classLoaderVOList); + process.appendResult(classloaderModel); + process.end(-1, "Found more than one classloader by class name, please specify classloader with '-c '"); + return; + } else { + process.end(-1, "Can not find classloader by class name: " + classLoaderClass + "."); + return; + } + } + if (all) { processAllClasses(process, inst); } else if (hashCode != null && resource != null) { diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderModel.java b/core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderModel.java index 36afd347..76c439a3 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderModel.java +++ b/core/src/main/java/com/taobao/arthas/core/command/model/ClassLoaderModel.java @@ -4,6 +4,7 @@ import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.ClassLoaderSta import java.util.List; import java.util.Map; +import java.util.Collection; /** * @author gongdewei 2020/4/21 @@ -20,6 +21,9 @@ public class ClassLoaderModel extends ResultModel { private Map classLoaderStats; + private Collection matchedClassLoaders; + private String classLoaderClass; + public ClassLoaderModel() { } @@ -90,4 +94,22 @@ public class ClassLoaderModel extends ResultModel { this.classLoaderStats = classLoaderStats; return this; } + + public String getClassLoaderClass() { + return classLoaderClass; + } + + public ClassLoaderModel setClassLoaderClass(String classLoaderClass) { + this.classLoaderClass = classLoaderClass; + return this; + } + + public Collection getMatchedClassLoaders() { + return matchedClassLoaders; + } + + public ClassLoaderModel setMatchedClassLoaders(Collection matchedClassLoaders) { + this.matchedClassLoaders = matchedClassLoaders; + return this; + } } diff --git a/core/src/main/java/com/taobao/arthas/core/command/view/ClassLoaderView.java b/core/src/main/java/com/taobao/arthas/core/command/view/ClassLoaderView.java index 359ca862..d433aca6 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/view/ClassLoaderView.java +++ b/core/src/main/java/com/taobao/arthas/core/command/view/ClassLoaderView.java @@ -22,6 +22,12 @@ public class ClassLoaderView extends ResultView { @Override public void draw(CommandProcess process, ClassLoaderModel result) { + if (result.getMatchedClassLoaders() != null) { + process.write("Matched classloaders: \n"); + drawClassLoaders(process, result.getMatchedClassLoaders(), false); + process.write("\n"); + return; + } if (result.getClassSet() != null) { drawAllClasses(process, result.getClassSet()); } diff --git a/site/src/site/sphinx/classloader.md b/site/src/site/sphinx/classloader.md index c76e3e0c..4f64ca90 100644 --- a/site/src/site/sphinx/classloader.md +++ b/site/src/site/sphinx/classloader.md @@ -16,6 +16,7 @@ classloader |[t]|打印所有ClassLoader的继承树| |[a]|列出所有ClassLoader加载的类,请谨慎使用| |`[c:]`|ClassLoader的hashcode| +|`[classLoaderClass:]`|指定执行表达式的 ClassLoader 的 class name| |`[c: r:]`|用ClassLoader去查找resource| |`[c: load:]`|用ClassLoader去加载指定的类| @@ -67,6 +68,18 @@ file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar Affect(row-cnt:9) cost in 3 ms. ``` +*注意* hashcode是变化的,需要先查看当前的ClassLoader信息,提取对应ClassLoader的hashcode。 + +对于只有唯一实例的ClassLoader可以通过class name指定,使用起来更加方便: + +```bash +$ classloader --classLoaderClass sun.misc.Launcher$AppClassLoader +file:/private/tmp/arthas-demo.jar +file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar + +Affect(row-cnt:9) cost in 3 ms. +``` + #### 使用ClassLoader去查找resource ```bash diff --git a/site/src/site/sphinx/en/classloader.md b/site/src/site/sphinx/en/classloader.md index e67ffeae..1381fff0 100644 --- a/site/src/site/sphinx/en/classloader.md +++ b/site/src/site/sphinx/en/classloader.md @@ -13,6 +13,7 @@ View hierarchy, urls and classes-loading info for the class-loaders. |[t]|print classloader's hierarchy| |[a]|list all the classes loaded by all the classloaders (use it with great caution since the output can be huge)| |[c:]|print classloader's hashcode| +|`[classLoaderClass:]`| The class name of the ClassLoader that executes the expression. | |`[c: r:]`|using ClassLoader to search resource| |`[c: load:]`|using ClassLoader to load class| @@ -64,6 +65,18 @@ file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar Affect(row-cnt:9) cost in 3 ms. ``` +Note that the hashcode changes, you need to check the current ClassLoader information first, and extract the hashcode corresponding to the ClassLoader. + +For ClassLoader with only unique instance, it can be specified by class name, which is more convenient to use: + +```bash +$ classloader --classLoaderClass sun.misc.Launcher$AppClassLoader +file:/private/tmp/arthas-demo.jar +file:/Users/hengyunabc/.arthas/lib/3.0.5/arthas/arthas-agent.jar + +Affect(row-cnt:9) cost in 3 ms. +``` + #### Use the classloader to load resource ```bash