这是我参与更文挑战的第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 | -c | ClassLoader的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。
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.DelegatingClassLoader和jdk.internal.reflect.DelegatingClassLoader。
ClassUtils.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具体详细信息。