- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
Arthas提供dump命令来导出已加载类的 byte code 到特定目录,可以考虑用来排查线上代码是否是最新的,这个命令的原理和Arthas的jad命令基本一致的,唯一的差别就是dump会到导出原始字节码,jad命令返回反编译的字节码。
dump命令的关键点在于Instrumentation的addTransformer方法,通过指定ClassFileTransformer来导出指定类的字节码。
Instrumentation
public interface Instrumentation {
//添加一个ClassFileTransformer
//之后类加载时都会经过这个ClassFileTransformer转换
void addTransformer(ClassFileTransformer transformer, boolean canRetransform);
void addTransformer(ClassFileTransformer transformer);
//移除ClassFileTransformer
boolean removeTransformer(ClassFileTransformer transformer);
boolean isRetransformClassesSupported();
//将一些已经加载过的类重新拿出来经过注册好的ClassFileTransformer转换
//retransformation可以修改方法体,但是不能变更方法签名、增加和删除方法/类的成员属性
void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
boolean isRedefineClassesSupported();
//重新定义某个类
void redefineClasses(ClassDefinition... definitions)
throws ClassNotFoundException, UnmodifiableClassException;
boolean isModifiableClass(Class<?> theClass);
@SuppressWarnings("rawtypes")
Class[] getAllLoadedClasses();
@SuppressWarnings("rawtypes")
Class[] getInitiatedClasses(ClassLoader loader);
long getObjectSize(Object objectToSize);
void appendToBootstrapClassLoaderSearch(JarFile jarfile);
void appendToSystemClassLoaderSearch(JarFile jarfile);
boolean isNativeMethodPrefixSupported();
void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix);
}
Instrumention支持的功能都在java.lang.instrument.Instrumentation接口中体现。
我们通过addTransformer方法注册了一个ClassFileTransformer,后面类加载的时候都会经过这个Transformer处理。对于已加载过的类,可以调用retransformClasses来重新触发这个Transformer的转换。
ClassFileTransformer可以判断是否需要修改类定义并根据自己的代码规则修改类定义然后返回给JVM。利用这个Transformer类,我们可以很好的实现虚拟机层面的AOP。
public class DumpClassCommand extends AnnotatedCommand {
public void process(CommandProcess process) {
RowAffect effect = new RowAffect();
try {
Instrumentation inst = process.session().getInstrumentation();
// 查找待导出的类
Set<Class<?>> matchedClasses = SearchUtils.searchClass(inst, classPattern, isRegEx, code);
if (matchedClasses == null || matchedClasses.isEmpty()) {
status = processNoMatch(process);
} else if (matchedClasses.size() > limit) {
status = processMatches(process, matchedClasses);
} else {
// 执行类的导出
status = processMatch(process, effect, inst, matchedClasses);
}
process.appendResult(new RowAffectModel(effect));
CommandUtils.end(process, status);
} catch (Throwable e){
}
}
private ExitStatus processMatch(CommandProcess process, RowAffect effect, Instrumentation inst, Set<Class<?>> matchedClasses) {
try {
// 执行dump操作
Map<Class<?>, File> classFiles = dump(inst, matchedClasses);
// 处理dump后的结果
List<DumpClassVO> dumpedClasses = new ArrayList<DumpClassVO>(classFiles.size());
for (Map.Entry<Class<?>, File> entry : classFiles.entrySet()) {
Class<?> clazz = entry.getKey();
File file = entry.getValue();
DumpClassVO dumpClassVO = new DumpClassVO();
dumpClassVO.setLocation(file.getCanonicalPath());
ClassUtils.fillSimpleClassVO(clazz, dumpClassVO);
dumpedClasses.add(dumpClassVO);
}
process.appendResult(new DumpClassModel().setDumpedClasses(dumpedClasses));
effect.rCnt(classFiles.keySet().size());
return ExitStatus.success();
} catch (Throwable t) {
}
}
private Map<Class<?>, File> dump(Instrumentation inst, Set<Class<?>> classes) throws UnmodifiableClassException {
// 通过ClassDumpTransformer
ClassDumpTransformer transformer = null;
if (directory != null) {
transformer = new ClassDumpTransformer(classes, new File(directory));
} else {
transformer = new ClassDumpTransformer(classes);
}
// 执行类的导出
InstrumentationUtils.retransformClasses(inst, transformer, classes);
return transformer.getDumpResult();
}
}
dump的核心流程在于查找对应的class类并执行dump动作。
dump内部是通过Instrument来进行触发ClassDumpTransformer动作,内部执行字节码的保存。
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 {
// 如果是待导出的类执行dumpClassIfNecessary操作
if (classesToEnhance.contains(classBeingRedefined)) {
dumpClassIfNecessary(classBeingRedefined, classfileBuffer);
}
return null;
}
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);
}
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的重写了transform方法,内部执行dumpClassIfNecessary保存字节码。
public class InstrumentationUtils {
public static void retransformClasses(Instrumentation inst, ClassFileTransformer transformer,
Set<Class<?>> classes) {
try {
inst.addTransformer(transformer, true);
for (Class<?> clazz : classes) {
if (ClassUtils.isLambdaClass(clazz)) {
logger.info(
"ignore lambda class: {}, because jdk do not support retransform lambda class: https://github.com/alibaba/arthas/issues/1512.",
clazz.getName());
continue;
}
try {
// 执行retransformClasses动作触发类
inst.retransformClasses(clazz);
} catch (Throwable e) {
String errorMsg = "retransformClasses class error, name: " + clazz.getName();
logger.error(errorMsg, e);
}
}
} finally {
inst.removeTransformer(transformer);
}
}
public static void trigerRetransformClasses(Instrumentation inst, Collection<String> classes) {
for (Class<?> clazz : inst.getAllLoadedClasses()) {
if (classes.contains(clazz.getName())) {
try {
inst.retransformClasses(clazz);
} catch (Throwable e) {
String errorMsg = "retransformClasses class error, name: " + clazz.getName();
logger.error(errorMsg, e);
}
}
}
}
}
retransformClasses先通过addTransformer来添加ClassDumpTransformer对象。
retransformClasses再通过retransformClasses来触发指定类的重新加载,重新加载过程中会触发ClassDumpTransformer的逻辑来实现字节码dump过程。
retransformClasses最后通过removeTransformer移除ClassDumpTransformer对象。
我想要显示正在加载的 .gif,直到所有内容都已加载,包括嵌入的 iframe。但是,目前加载 gif 会在除 iframe 之外的所有内容都已加载后消失。我怎样才能让它等到 iframe 也加载完毕
首先,这是我第一次接触 Angular。 我想要实现的是,我有一个通知列表,我必须以某种方式限制 limitTo,因此元素被限制为三个,在我单击按钮后,其余的应该加载。 我不明白该怎么做: 设置“ V
我正在尝试在我的设备上运行这个非常简单的应用程序(使用 map API V2),并且出于某种原因尝试使用 MapView 时: 使用 java 文件: public class MainMap e
我正在使用 Python 2.6、Excel 2007 Professional 和最新版本的 PyXLL。在 PyXLL 中加载具有 import scipy 抛出异常,模块未加载。有没有人能够在
我想做这个: 创建并打包原始游戏。然后我想根据原始游戏中的蓝图创建具有新网格/声音/动画和蓝图的其他 PAK 文件。原始游戏不应该知道有关其他网格/动画/等的任何信息。因此,我需要在原始游戏中使用 A
**摘要:**在java项目中经常会使用到配置文件,这里就介绍几种加载配置文件的方法。 本文分享自华为云社区《【Java】读取/加载 properties配置文件的几种方法》,作者:Copy工程师。
在 Groovy 脚本中是否可以执行条件导入语句? if (test){ import this.package.class } else { import that.package.
我正在使用 NVidia 视觉分析器(来自 CUDA 5.0 beta 版本的基于 eclipse 的版本)和 Fermi 板,我不了解其中两个性能指标: 全局加载/存储效率表示实际内存事务数与请求事
有没有办法在通过 routeProvider 加载特定 View 时清除 Angular JS 存储的历史记录? ? 我正在使用 Angular 创建一个公共(public)安装,并且历史会积累很多,
使用 Xcode 4.2,在我的应用程序中, View 加载由 segue 事件触发。 在 View Controller 中首先调用什么方法? -(void) viewWillAppear:(BOO
我在某些Django模型中使用JSONField,并希望将此数据从Oracle迁移到Postgres。 到目前为止,当使用Django的dumpdata和loaddata命令时,我仍然没有运气来保持J
创建 Nib 时,我需要创建两种类型:WindowNib 或 ViewNib。我看到的区别是,窗口 Nib 有一个窗口和一个 View 。 如何将 View Nib 加载到另一个窗口中?我是否必须创建
我想将多个env.variables转换为静态结构。 我可以手动进行: Env { is_development: env::var("IS_DEVELOPMENT")
正如我从一个测试用例中看到的:https://godbolt.org/z/K477q1 生成的程序集加载/存储原子松弛与普通变量相同:ldr 和 str 那么,宽松的原子变量和普通变量之间有什么区别吗
我有一个重定向到外部网站的按钮/链接,但是外部网站需要一些时间来加载。所以我想添加一个加载屏幕,以便外部页面在显示之前完全加载。我无法控制外部网站,并且外部网站具有同源策略,因此我无法在 iFrame
我正在尝试为我的应用程序开发一个Dockerfile,该文件在初始化后加载大量环境变量。不知何故,当我稍后执行以下命令时,这些变量是不可用的: docker exec -it container_na
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我刚刚遇到一个问题,我有一个带有一些不同选项的选择标签。 现在我想检查用户选择了哪些选项。 然后我想将一个新的 html 文件加载到该网站(取决于用户选中的选项)宽度 javascript,我该怎么做
我知道两种保存/加载应用程序设置的方法: 使用PersistentStore 使用文件系统(存储,因为 SDCard 是可选的) 我想知道您使用应用程序设置的做法是什么? 使用 PersistentS
我开始使用 Vulkan 时偶然发现了我的第一个问题。尝试创建调试报告回调时(验证层和调试扩展在我的英特尔 hd vulkan 驱动程序上可用,至少它是这么说的),它没有告诉我 vkCreateDeb
我是一名优秀的程序员,十分优秀!