mirror of
https://gitee.com/arthas/arthas.git
synced 2024-12-02 04:08:34 +08:00
classloader command support url statistics. #2095
This commit is contained in:
parent
f8a4cd5723
commit
851c2948e5
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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()))
|
||||
|
Loading…
Reference in New Issue
Block a user