ArthasArthas 类查找和反编译原理
Posted 九师兄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ArthasArthas 类查找和反编译原理相关的知识,希望对你有一定的参考价值。
1.概述
2.开篇
Arthas支持通过类相关的操作命令,包括sc、sm、jad等。
sc(Search-Class)
命令搜索出所有已经加载到 JVM 中的 Class 信息。
sm(Search-Method)
命令搜索出所有已经加载了 Class 信息的方法信息。
jad
命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码。
这篇文章是分析上述类相关操作的底层实现原理包含Instrumentation和CFR。
3.SC实现原理
public class SearchClassCommand extends AnnotatedCommand
public void process(final CommandProcess process)
// 1、核心是获取Instrumentation对象
Instrumentation inst = process.session().getInstrumentation();
// 2、SearchUtils负责查找对应的class
List<Class<?>> matchedClasses = new ArrayList<Class<?>>(SearchUtils.searchClass(inst, classPattern, isRegEx, hashCode));
// 3、对查询结果进行下排序
Collections.sort(matchedClasses, new Comparator<Class<?>>()
@Override
public int compare(Class<?> c1, Class<?> c2)
return StringUtils.classname(c1).compareTo(StringUtils.classname(c2));
);
affect.rCnt(matchedClasses.size());
process.appendResult(new RowAffectModel(affect));
process.end();
public class SearchUtils
public static Set<Class<?>> searchClass(Instrumentation inst, Matcher<String> classNameMatcher, int limit)
if (classNameMatcher == null)
return Collections.emptySet();
final Set<Class<?>> matches = new HashSet<Class<?>>();
// 通过inst.getAllLoadedClasses获取所有加载的类
for (Class<?> clazz : inst.getAllLoadedClasses())
if (classNameMatcher.matching(clazz.getName()))
matches.add(clazz);
if (matches.size() >= limit)
break;
return matches;
java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序。这种监测和协助包括但不限于获取JVM运行时状态,替换和修改类定义等。
Arthas的SC命令就是通过Instrumentation的getAllLoadedClasses来实现类的查找。
4. SM实现原理
public class SearchMethodCommand extends AnnotatedCommand
public void process(CommandProcess process)
RowAffect affect = new RowAffect();
Instrumentation inst = process.session().getInstrumentation();
Matcher<String> methodNameMatcher = methodNameMatcher();
// 1、通过Instrumentation查找对应的类
Set<Class<?>> matchedClasses = SearchUtils.searchClass(inst, classPattern, isRegEx, hashCode);
for (Class<?> clazz : matchedClasses)
try
// 2、遍历类的构造函数
for (Constructor<?> constructor : clazz.getDeclaredConstructors())
if (!methodNameMatcher.matching("<init>"))
continue;
MethodVO methodInfo = ClassUtils.createMethodInfo(constructor, clazz, isDetail);
process.appendResult(new SearchMethodModel(methodInfo, isDetail));
affect.rCnt(1);
// 3、遍历所有的方法
for (Method method : clazz.getDeclaredMethods())
if (!methodNameMatcher.matching(method.getName()))
continue;
MethodVO methodInfo = ClassUtils.createMethodInfo(method, clazz, isDetail);
process.appendResult(new SearchMethodModel(methodInfo, isDetail));
affect.rCnt(1);
catch (Error e)
process.appendResult(new RowAffectModel(affect));
process.end();
Arthas的SM命令首先通过Instrumentation的getAllLoadedClasses来实现类的查找。
Arthas的SM命令其次通过反射查找对应的方法。
5.JAD实现原理
public class JadCommand extends AnnotatedCommand
public void process(CommandProcess process)
RowAffect affect = new RowAffect();
Instrumentation inst = process.session().getInstrumentation();
// 1、通过Instrumentation查找对应的类
Set<Class<?>> matchedClasses = SearchUtils.searchClassOnly(inst, classPattern, isRegEx, code);
try
ExitStatus status = null;
if (matchedClasses == null || matchedClasses.isEmpty())
status = processNoMatch(process);
else if (matchedClasses.size() > 1)
status = processMatches(process, matchedClasses);
else // matchedClasses size is 1
// find inner classes.
Set<Class<?>> withInnerClasses = SearchUtils.searchClassOnly(inst, matchedClasses.iterator().next().getName() + "$*", false, code);
if(withInnerClasses.isEmpty())
withInnerClasses = matchedClasses;
// 2、执行类的反编译核心操作
status = processExactMatch(process, affect, inst, matchedClasses, withInnerClasses);
if (!this.sourceOnly)
process.appendResult(new RowAffectModel(affect));
CommandUtils.end(process, status);
catch (Throwable e)
private ExitStatus processExactMatch(CommandProcess process, RowAffect affect, Instrumentation inst, Set<Class<?>> matchedClasses, Set<Class<?>> withInnerClasses)
Class<?> c = matchedClasses.iterator().next();
Set<Class<?>> allClasses = new HashSet<Class<?>>(withInnerClasses);
allClasses.add(c);
try
// 1、创建ClassDumpTransformer对象
ClassDumpTransformer transformer = new ClassDumpTransformer(allClasses);
// 2、执行retransformClasses收集待反编译的类文件
InstrumentationUtils.retransformClasses(inst, transformer, allClasses);
Map<Class<?>, File> classFiles = transformer.getDumpResult();
File classFile = classFiles.get(c);
// 3、执行反编译的动作
Pair<String,NavigableMap<Integer,Integer>> decompileResult = Decompiler.decompileWithMappings(classFile.getAbsolutePath(), methodName, hideUnicode, lineNumber);
// 省略无关代码
return ExitStatus.success();
catch (Throwable t)
通过Instrumentation的getAllLoadedClasses来实现类的查找。
创建ClassDumpTransformer并通过retransformClasses保存原始字节码。
通过decompileWithMappings实现字节码的反编译。
class ClassDumpTransformer implements ClassFileTransformer
private Set<Class<?>> classesToEnhance;
private Map<Class<?>, File> dumpResult;
private File arthasLogHome;
private File directory;
public ClassDumpTransformer(Set<Class<?>> classesToEnhance)
this(classesToEnhance, null);
public ClassDumpTransformer(Set<Class<?>> classesToEnhance, File directory)
this.classesToEnhance = classesToEnhance;
this.dumpResult = new HashMap<Class<?>, File>();
this.arthasLogHome = new File(LogUtil.loggingDir());
this.directory = directory;
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws IllegalClassFormatException
if (classesToEnhance.contains(classBeingRedefined))
dumpClassIfNecessary(classBeingRedefined, classfileBuffer);
return null;
public Map<Class<?>, File> getDumpResult()
return dumpResult;
private void dumpClassIfNecessary(Class<?> clazz, byte[] data)
String className = clazz.getName();
ClassLoader classLoader = clazz.getClassLoader();
String classDumpDir = "classdump";
// 创建类所在的包路径
File dumpDir = null;
if (directory != null)
dumpDir = directory;
else
dumpDir = new File(arthasLogHome, classDumpDir);
if (!dumpDir.mkdirs() && !dumpDir.exists())
logger.warn("create dump directory: failed.", dumpDir.getAbsolutePath());
return;
String fileName;
if (classLoader != null)
fileName = classLoader.getClass().getName() + "-" + Integer.toHexString(classLoader.hashCode()) +
File.separator + className.replace(".", File.separator) + ".class";
else
fileName = className.replace(".", File.separator) + ".class";
File dumpClassFile = new File(dumpDir, fileName);
// 将类字节码写入文件
try
FileUtils.writeByteArrayToFile(dumpClassFile, data);
dumpResult.put(clazz, dumpClassFile);
catch (IOException e)
ClassDumpTransformer是自定义的Transformer对象,retransformClasses会进行调用。
ClassDumpTransformer的dumpClassIfNecessary负责保存原始的字节码。
public class InstrumentationUtils
public static void retransformClasses(Instrumentation inst, ClassFileTransformer transformer,
Set<Class<?>> classes)
try
inst.addTransformer(transformer, true);
for (Class<?> clazz : classes)
try
inst.retransformClasses(clazz);
catch (Throwable e)
String errorMsg = "retransformClasses class error, name: " + clazz.getName();
logger.error(errorMsg, e);
finally
inst.removeTransformer(transformer);
InstrumentationUtils负责执行原始字节码的收集,通过retransformClasses来实现。
public class Decompiler
public static Pair<String, NavigableMap<Integer, Integer>> decompileWithMappings(String classFilePath,
String methodName, boolean hideUnicode, boolean printLineNumber)
final StringBuilder sb = new StringBuilder(8192);
final NavigableMap<Integer, Integer> lineMapping = new TreeMap<Integer, Integer>();
OutputSinkFactory mySink = new OutputSinkFactory()
@Override
public List<SinkClass> getSupportedSinks(SinkType sinkType, Collection<SinkClass> collection)
return Arrays.asList(SinkClass.STRING, SinkClass.DECOMPILED, SinkClass.DECOMPILED_MULTIVER,
SinkClass.EXCEPTION_MESSAGE, SinkClass.LINE_NUMBER_MAPPING);
@Override
public <T> Sink<T> getSink(final SinkType sinkType, final SinkClass sinkClass)
return new Sink<T>()
@Override
public void write(T sinkable)
// skip message like: Analysing type demo.MathGame
if (sinkType == SinkType.PROGRESS)
以上是关于ArthasArthas 类查找和反编译原理的主要内容,如果未能解决你的问题,请参考以下文章