classloader command support url statistics. #2095

This commit is contained in:
hengyunabc 2022-03-03 00:37:46 +08:00
parent f8a4cd5723
commit 851c2948e5
3 changed files with 132 additions and 0 deletions

View File

@ -24,6 +24,8 @@ import com.taobao.middleware.cli.annotations.Summary;
import java.lang.instrument.Instrumentation;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -51,6 +53,7 @@ import java.util.TreeSet;
" classloader -a\n" +
" classloader -a -c 327a647b\n" +
" classloader -c 659e0bfd --load demo.MathGame\n" +
" classloader -u # url statistics\n" +
Constants.WIKI + Constants.WIKI_HOME + "classloader")
public class ClassLoaderCommand extends AnnotatedCommand {
@ -63,6 +66,8 @@ public class ClassLoaderCommand extends AnnotatedCommand {
private boolean includeReflectionClassLoader = true;
private boolean listClassLoader = false;
private boolean urlStat = false;
private String loadClass = null;
private volatile boolean isInterrupted = false;
@ -115,6 +120,12 @@ public class ClassLoaderCommand extends AnnotatedCommand {
this.loadClass = className;
}
@Option(shortName = "u", longName = "url-stat", flag = true)
@Description("Display classloader url statistics")
public void setUrlStat(boolean urlStat) {
this.urlStat = urlStat;
}
@Override
public void process(CommandProcess process) {
// ctrl-C support
@ -123,6 +134,15 @@ public class ClassLoaderCommand extends AnnotatedCommand {
boolean classLoaderSpecified = false;
Instrumentation inst = process.session().getInstrumentation();
if (urlStat) {
Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats = this.urlStats(inst);
ClassLoaderModel model = new ClassLoaderModel();
model.setUrlStats(urlStats);
process.appendResult(model);
process.end();
return;
}
if (hashCode != null || classLoaderClass != null) {
classLoaderSpecified = true;
@ -396,6 +416,42 @@ public class ClassLoaderCommand extends AnnotatedCommand {
}
return urlStrs;
}
private Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats(Instrumentation inst) {
Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats = new HashMap<ClassLoaderVO, ClassLoaderUrlStat>();
Map<ClassLoader, Set<String>> usedUrlsMap = new HashMap<ClassLoader, Set<String>>();
for (Class<?> clazz : inst.getAllLoadedClasses()) {
ClassLoader classLoader = clazz.getClassLoader();
if (classLoader != null) {
ProtectionDomain protectionDomain = clazz.getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
if (codeSource != null) {
URL location = codeSource.getLocation();
if (location != null) {
Set<String> urls = usedUrlsMap.get(classLoader);
if (urls == null) {
urls = new HashSet<String>();
usedUrlsMap.put(classLoader, urls);
}
urls.add(location.toString());
}
}
}
}
for (Entry<ClassLoader, Set<String>> entry : usedUrlsMap.entrySet()) {
ClassLoader loader = entry.getKey();
Set<String> usedUrls = entry.getValue();
List<String> allUrls = getClassLoaderUrls(loader);
List<String> unusedUrls = new ArrayList<String>();
for (String url : allUrls) {
if (!usedUrls.contains(url)) {
unusedUrls.add(url);
}
}
urlStats.put(ClassUtils.createClassLoaderVO(loader), new ClassLoaderUrlStat(usedUrls, unusedUrls));
}
return urlStats;
}
// 以树状列出ClassLoader的继承结构
private static List<ClassLoaderVO> processClassLoaderTree(List<ClassLoaderVO> classLoaders) {
@ -583,6 +639,36 @@ public class ClassLoaderCommand extends AnnotatedCommand {
}
}
public static class ClassLoaderUrlStat {
private Collection<String> usedUrls;
private Collection<String> unUsedUrls;
public ClassLoaderUrlStat() {
}
public ClassLoaderUrlStat(Collection<String> usedUrls, Collection<String> unUsedUrls) {
super();
this.usedUrls = usedUrls;
this.unUsedUrls = unUsedUrls;
}
public Collection<String> getUsedUrls() {
return usedUrls;
}
public void setUsedUrls(Collection<String> usedUrls) {
this.usedUrls = usedUrls;
}
public Collection<String> getUnUsedUrls() {
return unUsedUrls;
}
public void setUnUsedUrls(Collection<String> unUsedUrls) {
this.unUsedUrls = unUsedUrls;
}
}
public static class ClassLoaderStat {
private int loadedCount;
private int numberOfInstance;

View File

@ -1,6 +1,7 @@
package com.taobao.arthas.core.command.model;
import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.ClassLoaderStat;
import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.ClassLoaderUrlStat;
import java.util.List;
import java.util.Map;
@ -24,6 +25,9 @@ public class ClassLoaderModel extends ResultModel {
private Collection<ClassLoaderVO> matchedClassLoaders;
private String classLoaderClass;
//urls stat
private Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats;
public ClassLoaderModel() {
}
@ -112,4 +116,13 @@ public class ClassLoaderModel extends ResultModel {
this.matchedClassLoaders = matchedClassLoaders;
return this;
}
public Map<ClassLoaderVO, ClassLoaderUrlStat> getUrlStats() {
return urlStats;
}
public void setUrlStats(Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats) {
this.urlStats = urlStats;
}
}

View File

@ -1,6 +1,7 @@
package com.taobao.arthas.core.command.view;
import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.ClassLoaderStat;
import com.taobao.arthas.core.command.klass100.ClassLoaderCommand.ClassLoaderUrlStat;
import com.taobao.arthas.core.command.model.ClassDetailVO;
import com.taobao.arthas.core.command.model.ClassLoaderModel;
import com.taobao.arthas.core.command.model.ClassLoaderVO;
@ -14,6 +15,7 @@ import com.taobao.text.util.RenderUtil;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* @author gongdewei 2020/4/21
@ -46,8 +48,39 @@ public class ClassLoaderView extends ResultView<ClassLoaderModel> {
if (result.getClassLoaderStats() != null){
drawClassLoaderStats(process, result.getClassLoaderStats());
}
if (result.getUrlStats() != null) {
drawUrlStats(process, result.getUrlStats());
}
}
private void drawUrlStats(CommandProcess process, Map<ClassLoaderVO, ClassLoaderUrlStat> urlStats) {
for (Entry<ClassLoaderVO, ClassLoaderUrlStat> entry : urlStats.entrySet()) {
ClassLoaderVO classLoaderVO = entry.getKey();
ClassLoaderUrlStat urlStat = entry.getValue();
// 忽略 sun.reflect.DelegatingClassLoader 等动态ClassLoader
if (urlStat.getUsedUrls().isEmpty() && urlStat.getUnUsedUrls().isEmpty()) {
continue;
}
TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1);
table.row(new LabelElement(classLoaderVO.getName() + ", hash:" + classLoaderVO.getHash())
.style(Decoration.bold.bold()));
Collection<String> usedUrls = urlStat.getUsedUrls();
table.row(new LabelElement("Used URLs:").style(Decoration.bold.bold()));
for (String url : usedUrls) {
table.row(url);
}
Collection<String> UnnsedUrls = urlStat.getUnUsedUrls();
table.row(new LabelElement("Unused URLs:").style(Decoration.bold.bold()));
for (String url : UnnsedUrls) {
table.row(url);
}
process.write(RenderUtil.render(table, process.width()))
.write("\n");
}
}
private void drawClassLoaderStats(CommandProcess process, Map<String, ClassLoaderStat> classLoaderStats) {
Element element = renderStat(classLoaderStats);
process.write(RenderUtil.render(element, process.width()))