From 2f579cd353e1cd4d7b4afdd3c9f3a1b60fea5a70 Mon Sep 17 00:00:00 2001 From: gongdewei Date: Wed, 13 May 2020 20:48:49 +0800 Subject: [PATCH] slice the result of 'classloader -a' --- .../command/klass100/ClassLoaderCommand.java | 85 +++++++++++++------ .../core/command/model/ClassLoaderModel.java | 10 +-- .../arthas/core/command/model/ClassSetVO.java | 24 ++++-- .../core/command/view/ClassLoaderView.java | 22 +++-- 4 files changed, 92 insertions(+), 49 deletions(-) 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 3237b46db..239425757 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 @@ -6,6 +6,7 @@ import com.taobao.arthas.core.command.Constants; import com.taobao.arthas.core.command.model.*; 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.affect.RowAffect; import com.taobao.middleware.cli.annotations.Description; @@ -16,19 +17,9 @@ import com.taobao.middleware.cli.annotations.Summary; import java.lang.instrument.Instrumentation; import java.net.URL; import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicBoolean; @Name("classloader") @Summary("Show classloader info") @@ -54,6 +45,8 @@ public class ClassLoaderCommand extends AnnotatedCommand { private String loadClass = null; + private AtomicBoolean isInterrupted = new AtomicBoolean(false); + @Option(shortName = "t", longName = "tree", flag = true) @Description("Display ClassLoader tree") public void setTree(boolean tree) { @@ -98,6 +91,9 @@ public class ClassLoaderCommand extends AnnotatedCommand { @Override public void process(CommandProcess process) { + // ctrl-C support + process.interruptHandler(new ClassLoaderInterruptHandler(process, isInterrupted)); + Instrumentation inst = process.session().getInstrumentation(); if (all) { processAllClasses(process, inst); @@ -242,23 +238,30 @@ public class ClassLoaderCommand extends AnnotatedCommand { private void processAllClasses(CommandProcess process, Instrumentation inst) { RowAffect affect = new RowAffect(); - List allClasses = getAllClasses(hashCode, inst, affect); - process.appendResult(new ClassLoaderModel().setAllClasses(allClasses)); + getAllClasses(hashCode, inst, affect, process); + if (checkInterrupted(process)) { + return; + } process.appendResult(new RowAffectModel(affect)); process.end(); } + private boolean checkInterrupted(CommandProcess process) { + if (isInterrupted.get()) { + process.end(1, "Interrupted by user"); + return true; + } + return false; + } + /** * 获取到所有的class, 还有它们的classloader,按classloader归类好,统一输出每个classloader里有哪些class *

* 当hashCode是null,则把所有的classloader的都打印 * - * @param hashCode - * @param affect - * @return */ @SuppressWarnings("rawtypes") - private static List getAllClasses(String hashCode, Instrumentation inst, RowAffect affect) { + private void getAllClasses(String hashCode, Instrumentation inst, RowAffect affect, CommandProcess process) { int hashCodeInt = -1; if (hashCode != null) { hashCodeInt = Integer.valueOf(hashCode, 16); @@ -301,26 +304,39 @@ public class ClassLoaderCommand extends AnnotatedCommand { } //convert to vo - List classSets = new ArrayList(); - classSets.add(new ClassSetVO(ClassUtils.createClassLoaderVO(null), toClassNames(bootstrapClassSet))); + int sizeLimit = 1024; + processClassSet(process, ClassUtils.createClassLoaderVO(null), bootstrapClassSet, sizeLimit); affect.rCnt(bootstrapClassSet.size()); for (Entry> entry : classLoaderClassMap.entrySet()) { + if (checkInterrupted(process)) { + return; + } ClassLoader classLoader = entry.getKey(); SortedSet classSet = entry.getValue(); - List classNames = toClassNames(classSet); - classSets.add(new ClassSetVO(ClassUtils.createClassLoaderVO(classLoader), classNames)); + processClassSet(process, ClassUtils.createClassLoaderVO(classLoader), classSet, sizeLimit); affect.rCnt(classSet.size()); } - return classSets; } - private static List toClassNames(SortedSet classSet) { - List classNames = new ArrayList(classSet.size()); - for (Class aClass : classSet) { + private void processClassSet(CommandProcess process, ClassLoaderVO classLoaderVO, Collection classes, int sizeLimit) { + List classNames = new ArrayList(512); + int segment = 0; + for (Class aClass : classes) { classNames.add(aClass.getName()); + //slice segment + if(classNames.size() >= sizeLimit) { + if (checkInterrupted(process)) { + return; + } + process.appendResult(new ClassLoaderModel().setClassSet(new ClassSetVO(classLoaderVO, classNames, segment++))); + classNames = new ArrayList(512); + } + } + //last segment + if (classNames.size() > 0) { + process.appendResult(new ClassLoaderModel().setClassSet(new ClassSetVO(classLoaderVO, classNames, segment++))); } - return classNames; } private static List getClassLoaderUrls(ClassLoader classLoader) { @@ -566,4 +582,19 @@ public class ClassLoaderCommand extends AnnotatedCommand { return unsortedStats.get(o2).getLoadedCount() - unsortedStats.get(o1).getLoadedCount(); } } + + private class ClassLoaderInterruptHandler implements Handler { + private final CommandProcess process; + private final AtomicBoolean isInterrupted; + + public ClassLoaderInterruptHandler(CommandProcess process, AtomicBoolean isInterrupted) { + this.process = process; + this.isInterrupted = isInterrupted; + } + + @Override + public void handle(Void event) { + isInterrupted.set(true); + } + } } 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 8cc0a0e24..607a1b413 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 @@ -10,7 +10,7 @@ import java.util.Map; */ public class ClassLoaderModel extends ResultModel { - private List allClasses; + private ClassSetVO classSet; private List resources; private ClassVO loadClass; private List urls; @@ -28,12 +28,12 @@ public class ClassLoaderModel extends ResultModel { return "classloader"; } - public List getAllClasses() { - return allClasses; + public ClassSetVO getClassSet() { + return classSet; } - public ClassLoaderModel setAllClasses(List allClasses) { - this.allClasses = allClasses; + public ClassLoaderModel setClassSet(ClassSetVO classSet) { + this.classSet = classSet; return this; } diff --git a/core/src/main/java/com/taobao/arthas/core/command/model/ClassSetVO.java b/core/src/main/java/com/taobao/arthas/core/command/model/ClassSetVO.java index dc9541c6c..4755d3b7b 100644 --- a/core/src/main/java/com/taobao/arthas/core/command/model/ClassSetVO.java +++ b/core/src/main/java/com/taobao/arthas/core/command/model/ClassSetVO.java @@ -1,17 +1,23 @@ package com.taobao.arthas.core.command.model; -import java.util.List; +import java.util.Collection; /** * @author gongdewei 2020/4/21 */ public class ClassSetVO { private ClassLoaderVO classloader; - private List classes; + private Collection classes; + private int segment; - public ClassSetVO(ClassLoaderVO classloader, List classes) { + public ClassSetVO(ClassLoaderVO classloader, Collection classes) { + this(classloader, classes, 0); + } + + public ClassSetVO(ClassLoaderVO classloader, Collection classes, int segment) { this.classloader = classloader; this.classes = classes; + this.segment = segment; } public ClassLoaderVO getClassloader() { @@ -22,11 +28,19 @@ public class ClassSetVO { this.classloader = classloader; } - public List getClasses() { + public Collection getClasses() { return classes; } - public void setClasses(List classes) { + public void setClasses(Collection classes) { this.classes = classes; } + + public int getSegment() { + return segment; + } + + public void setSegment(int segment) { + this.segment = segment; + } } 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 48a2fe350..1cf5caeef 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 @@ -21,8 +21,8 @@ public class ClassLoaderView extends ResultView { @Override public void draw(CommandProcess process, ClassLoaderModel result) { - if (result.getAllClasses() != null) { - drawAllClasses(process, result.getAllClasses()); + if (result.getClassSet() != null) { + drawAllClasses(process, result.getClassSet()); } if (result.getResources() != null) { drawResources(process, result.getResources()); @@ -72,8 +72,9 @@ public class ClassLoaderView extends ResultView { process.write(RenderUtil.render(ClassUtils.renderClassInfo(loadClass), process.width()) + "\n"); } - private void drawAllClasses(CommandProcess process, List allClasses) { - process.write(RenderUtil.render(renderClasses(allClasses), process.width())); + private void drawAllClasses(CommandProcess process, ClassSetVO classSetVO) { + process.write(RenderUtil.render(renderClasses(classSetVO), process.width())); + process.write("\n"); } private void drawResources(CommandProcess process, List resources) { @@ -85,18 +86,15 @@ public class ClassLoaderView extends ResultView { process.write(com.taobao.arthas.core.util.Constants.EMPTY_STRING); } - private Element renderClasses(List allClasses) { + private Element renderClasses(ClassSetVO classSetVO) { TableElement table = new TableElement().leftCellPadding(1).rightCellPadding(1); - - for (ClassSetVO classSetVO : allClasses) { + if (classSetVO.getSegment() == 0) { table.row(new LabelElement("hash:" + classSetVO.getClassloader().getHash() + ", " + classSetVO.getClassloader().getName()) .style(Decoration.bold.bold())); - for (String className : classSetVO.getClasses()) { - table.row(new LabelElement(className)); - } - table.row(new LabelElement(" ")); } - + for (String className : classSetVO.getClasses()) { + table.row(new LabelElement(className)); + } return table; }