赞
踩
是java命令的参数, 该参数可以指定一个jar包 ,cmd命令格式如下: (javaagent的个数没有限制)
java -javaagent:<jar包路径> [-javaagent:<jar包路径>] -cp <要运行的jar包路径> <运行的类> [该类参数]...
main之前添加手脚:
META-INF/MANIFEST.MF文件需要指定Premain-Class字段, 并让该类实现premain方法 JDK1.5
main运行之后添加手脚:
META-INF/MANIFEST.MF文件需要指定Agent-Class字段, 并让该类实现agentmain方法 JDK1.6
当JVM虚拟机启动时, 在执行自己的jar包的main方法之前, 会先运行premain方法
premain方法有两种实现, 如果都实现了, JVM会优先使用带Instrumentation inst参数的
public static void premain(String agentArgs, Instrumentation inst)
public static void premain(String agentArgs)
Instrumentation接口定义如下:
public interface Instrumentation { //增加一个Class 文件的转换器,转换器用于改变 Class 二进制流的数据,参数 canRetransform 设置是否允许重新转换。 void addTransformer(ClassFileTransformer transformer, boolean canRetransform); //在类加载之前,重新定义 Class 文件,ClassDefinition 表示对一个类新的定义,如果在类加载之后,需要使用 retransformClasses 方法重新定义。addTransformer方法配置之后,后续的类加载都会被Transformer拦截。对于已经加载过的类,可以执行retransformClasses来重新触发这个Transformer的拦截。类加载的字节码被修改后,除非再次被retransform,否则不会恢复。 void addTransformer(ClassFileTransformer transformer); //删除一个类转换器 boolean removeTransformer(ClassFileTransformer transformer); //在类加载之后,重新定义 Class。这个很重要,该方法是1.6 之后加入的,事实上,该方法是 update 了一个类。 void retransformClasses(Class<?>... classes) throws UnmodifiableClassException; //获取一个对象的大小 long getObjectSize(Object objectToSize); ........ ........ }
premain方法会在大部分类的main方法之前执行(因为有一些系统类是需要先执行的). 因此 , 用户类的加载都可以被拦截的, 这样我们就可以做一些像重写类,或者监控等操作了
可以结合第三方的字节码编译工具 , 比如ASM , javaassist , cglib , bytebuddy 等等来改写class文件
在Java SE 6的Instrumentation当中,提供了一个新的代理操作方法:agentmain,可以在main函数开始运行之后再运行。
优先运行带Instrumentation inst参数的方法
// 采用attach机制,被代理的目标程序VM有可能很早之前已经启动,当然其所有类已经被加载完成,这个时候需要借助Instrumentation#retransformClasses(Class<?>... classes)让对应的类可以重新转换,从而激活重新转换的类执行ClassFileTransformer列表中的回调
public static void agentmain (String agentArgs, Instrumentation inst)
public static void agentmain (String agentArgs)
用Attach来实现此功能 : com.sun.tools.attach包下 有两个主要的类
VirtualMachine
字面意义表示一个Java 虚拟机,也就是程序需要监控的目标虚拟机,提供了获取系统信息(比如获取内存dump、线程dump,类信息统计(比如已加载的类以及实例个数等), loadAgent,Attach 和 Detach (Attach 动作的相反行为,从 JVM 上面解除一个代理)等方法,可以实现的功能可以说非常之强大 。该类允许我们通过给attach方法传入一个jvm的pid(进程id),远程连接到jvm上 。
代理类注入操作只是它众多功能中的一个,通过loadAgent方法向jvm注册一个代理程序agent,在该agent的代理程序中会得到一个Instrumentation实例,该实例可以 在class加载前改变class的字节码,也可以在class加载后重新加载。在调用Instrumentation实例的方法时,这些方法会使用ClassFileTransformer接口中提供的方法进行处理。
VirtualMachineDescriptor
则是一个描述虚拟机的容器类,配合VirtualMachine
类完成各种功能。
通过VirtualMachine类的attach(pid)方法,便可以attach到一个运行中的java进程上,之后便可以通过loadAgent(agentJarPath)来将agent的jar包注入到对应的进程,然后对应的进程会调用agentmain方法。
因此可知, 这里会有两个进程之间的连接
既然是两个进程之间通信那肯定的建立起连接,VirtualMachine.attach动作类似TCP创建连接的三次握手,目的就是搭建attach通信的连接。而后面执行的操作,例如vm.loadAgent,其实就是向这个socket写入数据流,接收方target VM会针对不同的传入数据来做不同的处理。
com.sun.tools.attach包 不能在JDK中直接提示的话, 可以手动从JAVA_HOME/lib下找到
参考资料: https://blog.csdn.net/qq_31279701/article/details/120710892
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。