Arthas 应用诊断利器学习及使用(九) 类命令sc,sm,classloader

761 阅读5分钟

这是我参与更文挑战的第7天,活动详情查看: 更文挑战

本文内容基于arthas 3.5.2 版本,介绍类命令sc,sm,classloader的使用及实现

一.sc命令

查看JVM已加载的类信息

参数名称参数缩写参数说明必填项
class-pattern名称表达式匹配必填
--details-d输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。如果一个类被多个ClassLoader所加载,则会出现多次可选
--field-f输出当前类的成员变量信息(需要配合参数-d一起使用)可选
--regex-E开启正则表达式匹配,默认为通配符匹配。可选
--expand-x指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 toString 输出可选
--classloader-c指定class的 ClassLoader 的 hashcode可选
--classLoaderClass指定执行表达式的 ClassLoader 的 class name可选
--limits-n具有详细信息的匹配类的最大数量(默认为100)可选

文件位于com.taobao.arthas.core.command.klass100.SearchClassCommand

以执行sc demo.MathGame为例 SearchUtils.searchClass(Instrumentation inst, "demo.MathGame", false, null) SearchUtils.classNameMatcher(String classPattern, boolean isRegEx)方法,如果不包含$$Lambda则将/替换为.。 SearchUtils.searchClass(Instrumentation inst, Matcher<String> classNameMatcher, int limit)方法检索被JVM加载的class。 Instrumentation getAllLoadedClasses()获取所有被加载的class ClassUtils.createClassInfo获取class的具体信息 StringUtils.classname获取class名 判断是否是数组,是的话,按照数组规则截取拼接class名 数组对象的类名以一个或多个’[‘开头,几维数组就有几个’[‘,数组元素类型若是基本类型会对应相应的编码,若是接口或类则是L+类名, 否的话,按照class.getName()来获取。

以下为ClassUtils.createClassInfo返回的内容。

class-info,name 通过StringUtils.classname获取为demo.MathGame
clazz.getProtectionDomain().getCodeSource()获取CodeSource,非空判断通过后,cs.getLocation().getFile()得到code-source具体路径
clazz.isInterface()判断是否是接口
clazz.isAnnotation()判断是否是注解
clazz.isEnum()判断是否是枚举类型
clazz.isAnonymousClass()是否是匿名类
clazz.isArray()是否是数组
clazz.isLocalClass()是否是本地类(定义在方法内部的类)
clazz.isMemberClass()是否是成员类
clazz.isPrimitive()是否为原始类型(boolean、char、byte、short、int、long、float、double)
clazz.isSynthetic()编译器生成的synthetic方法或者类出现在私有内部类或方法上
clazz.getSimpleName()类的简写名:MathGame
StringUtils.modifier(clazz.getModifiers(), ',')类的修饰符
clazz.getDeclaredAnnotations()获取类上的注解名,通过StringUtils.classname(annotation.annotationType())获取
clazz.getInterfaces()获取接口名
clazz.getSuperclass()获取父类
clazz.getClassLoader()获取classloader
Integer.toHexString(clazz.getClassLoader().hashCode())获取classloader hashcode
clazz.getDeclaredFields() 获取参数信息

二.sm命令

查看已加载类的方法信息

参数名称参数缩写参数说明必填项
class-pattern名称表达式匹配必填
method-pattern方法名表达式匹配可选
--details-d输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。如果一个类被多个ClassLoader所加载,则会出现多次可选
--regex-E开启正则表达式匹配,默认为通配符匹配。可选
--classloader-c指定class的 ClassLoader 的 hashcode可选
--classLoaderClass指定执行表达式的 ClassLoader 的 class name可选
--limits-n具有详细信息的匹配类的最大数量(默认为100)可选

文件位于com.taobao.arthas.core.command.klass100.SearchMethodCommand

ClassUtils.createMethodInfo获取具体信息与sc命令类似 method.getDeclaredAnnotations()获取注解 method.getParameterTypes()获取参数类型 method.getReturnType()获取返回类型 method.getExceptionTypes()获取异常类型 StringUtils.classLoaderHash(clazz)获取classloader的hascode

三.classloader命令

查看classloader的继承树,urls,类加载信息

参数名称参数缩写参数说明必填项
--tree-t打印所有ClassLoader的继承树可选
--classLoaderClass指定执行表达式的 ClassLoader 的 class name可选
--classloader-cClassLoader的hashcode可选
--all-a列出所有ClassLoader加载的类,请谨慎使用可选
--resource-r用ClassLoader去查找resource,需要与-c配合使用可选
--includeReflectionClassLoader-i列出的classloader中是否需要列出sun.reflect.DelegatingClassLoader,当参数包含-l或者-t时生效可选
--list-classloader-l按类加载实例进行统计可选
--load用ClassLoader去加载指定的类,需要与-c配合使用可选

文件位于com.taobao.arthas.core.command.klass100.ClassLoaderCommand.java

image.png

processClassLoaderStats() 统计classloader的类加载情况 getAllClassLoaderInfo() 获取所有classloader信息 循环inst.getAllLoadedClasses(),获取classloader clazz.getClassLoader() classLoader.getParent()获取父级classloader

排序时,把用户自己定的ClassLoader排在最前面,以sun.开头的放后面,因为sun.reflect.DelegatingClassLoader的实例太多。

执行classloader -a,进入分支processAllClasses() 列出所有classloader加载的类。 获取到所有的class, 还有它们的classloader,按classloader归类好,统一输出每个classloader里有哪些class,当hashCode是null,则把所有的classloader的都打印。

当执行命令参数有-l或者-t时执行processClassLoaders(),参数没有-i(官网3.5.1中文档缺失-i说明 包含sun.reflect.DelegatingClassLoader)的情况下 执行getAllClassLoaderInfo(inst, new SunReflectionClassLoaderFilter()) SunReflectionClassLoaderFilter中排除sun.reflect.DelegatingClassLoaderjdk.internal.reflect.DelegatingClassLoaderClassUtils.createClassLoaderVO生成VO类用于结果展示,若有-t参数则执行processClassLoaderTree()方法以树状列出ClassLoader的继承结构,递归调用buildTree组织成需要的树状格式。

参数-c ClassLoader的hashcode 参数-classLoaderClass: 指定执行表达式的 ClassLoader 的 class name 这两个参数使用到其中任意一个时classLoaderSpecified设置为true 如果按照hashCode查询找到对应hashCode的targetClassLoader 通过类名查找classloader,ClassLoaderUtils.getClassLoaderByClassName,若匹配上的classloader只有一个继续向下执行,多个classloader直接报错提示返回。

执行processClassLoader(),根据 ClassLoader 来打印URLClassLoader的urls [c: r:] 用ClassLoader去查找resource 执行processResources() targetClassLoader.getResources获取resource路径。

[c: load:] 用ClassLoader去加载指定的类 执行processLoadClass() targetClassLoader.loadClass直接加载class,并获取class具体详细信息。