java.lang.Object
cn.wjybxx.agent.ClassReloadAgent
Instrumentation开发指南
IBM文档
热更新限制
1. 热更新只可以更改方法体和常量池,不可以增删属性和方法,不可以修改继承关系。 2. 已初始化的类不会再次进行初始化(注意静态代码块)。 3. 热更的方法,只有再次进入时才会生效。 4. 被内联的方法可能提示热更新成功,却永远得不到执行(注意热点代码)。 5. class对象引用不会改变,即不需要向其它热更新方式那样迁移数据(这是很大的优势)。 6. 不可增删lambda表达式,不可以增删方法引用。 7. 内部类和外部类必须一起热更新。违背直觉的情况
1. lambda表达式:如果lambda表达式捕获的变量变更,将无法热更(因为编译时会生成特殊的粘合类,粘合类的成员属性会变更)。 2. 内部类:如果需要访问另一个类的private字段,将无法热更(因为编译时会为其生成特殊的桥接方法,新增了静态方法)。 3. switch:大型的switch语句无法热更(大型switch语句建议使用map进行映射)。奇巧淫技
1. 每个manager额外定义一个通用方法Object execute(String cmd, Object params),当需要某个manager的功能和属性时,可以迂回救国。
2. 每个manager额外定义一个黑板,比如就一个Map,这样当需要新增属性时,可以添加到map中。加上上一条,最好有个manager基类?
3. 每个玩家额外定义两个黑板,比如两个map,一个存库,一个不存库。这样当需要在玩家身上新增属性时,可以存储到map中。
4. 玩家与服务器之间可以预留几条通用协议,用于救急。
5. 内部类的属性尽量声明为包级(默认权限),尽量少使用private,或提供getter/setter方法。
使用方式
1. 由于代理必须以jar包形式存在,因此文件检测,执行更新等逻辑,请写在自己的业务逻辑包中,不要写在这里,方便扩展。 2. 热更新时不要一次更新太多类,否则可能导致停顿时间过长。 3. 由于该API于IDE热更新是一套API,因此必须在本机上进行热更新测试,本机能通过,基本上运行环境也能通过。 4. 每次启服后,需要进行一次热更流程,避免使用的是旧的class文件。 5. 除非重启服务器,否则热更的代码不可删除。因此,只有在更版本的时候才可以删除热更代码,替换为正式的代码(这也是启服后必须执行一次热更的原因)。 6. 热更只应该用于修改重大bug,不建议动不动就热更,平时要保证代码质量。
debug下使用agentmain(String, Instrumentation)比较方便,直接在ide中添加启动参数就可以。
线上使用premain(String, Instrumentation)方式比较方便。
- 作者:
- wjybxx date 2023/9/9
-
构造器概要
构造器 -
方法概要
修饰符和类型方法说明static voidagentmain(String agentArgs, Instrumentation instrumentation) 使用动态attach的方式获取Instrumentation。static void添加指定jar包到根类加载器路径中static voidappendToSystemClassLoaderSearch(JarFile jarfile) 添加指定jar包到系统类加载器路径中static booleanisModifiableClass(Class<?> theClass) 查询一个类是否可以被修改(被重定义),是否可以用于redefineClasses(ClassDefinition...)方法。static boolean查询是否启用了重定义类功能。static voidpremain(String agentArgs, Instrumentation instrumentation) 这是instrument开发规范规定的固定格式的方法,当java程序启动时,如果指定了javaagent参数(classpath下的jar包名字),则会自动调用到这个方法。static voidredefineClasses(ClassDefinition... definitions) 重定义类文件(热更新类文件) 注意:请保证isRedefineClassesSupported()为true,且所有要更新的类isModifiableClass(Class)为true。
-
构造器详细资料
-
ClassReloadAgent
public ClassReloadAgent()
-
-
方法详细资料
-
premain
这是instrument开发规范规定的固定格式的方法,当java程序启动时,如果指定了javaagent参数(classpath下的jar包名字),则会自动调用到这个方法。 注意: 1. 需要在启动参数中指定 javaagent参数。eg: -javaagent:game-classreloadagent-1.0.jar=test 则agentArgs为test(若无等号则为null) 2. 不能方便的调试,必须在命令行中启动。- 参数:
agentArgs- 启动参数instrumentation- 我们需要的实例,需要将其保存下来
-
agentmain
使用动态attach的方式获取Instrumentation。 这是instrument开发规范规定的固定格式的方法,当使用VirtualMachine.loadAgent(String, String)连接到JVM时,会触发该方法。注意 1. 如果要attach到自身所在JVM,需要添加启动参数 -Djdk.attach.allowAttachSelf=true 否则会抛出异常。 2. 它的参数来自
VirtualMachine.loadAgent(String, String)的第二个参数(options) 3. 可直接在debug环境下使用(在debug参数中指定vm options即可)。- 参数:
agentArgs-VirtualMachine.loadAgent(String, String)中的optionsinstrumentation- 我们需要的实例,需要将其保存下来
-
isRedefineClassesSupported
public static boolean isRedefineClassesSupported()查询是否启用了重定义类功能。 注意:该返回值与isModifiableClass(Class)返回值没有关系。 -
isModifiableClass
查询一个类是否可以被修改(被重定义),是否可以用于redefineClasses(ClassDefinition...)方法。 注意:该返回值与isRedefineClassesSupported()的返回值没有关系。 所以:要热更新类时需要保证isRedefineClassesSupported()为true,且要更新的类调用当前方法返回值为true。 基本上:你在ide中能热更新,那么这里就能热更新,所以最好现在ide上测试一下。- 参数:
theClass- 要热更新的类- 返回:
- true/false
-
redefineClasses
public static void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException 重定义类文件(热更新类文件) 注意:请保证isRedefineClassesSupported()为true,且所有要更新的类isModifiableClass(Class)为true。- 参数:
definitions- 要热更新的类(要重定义的类) 注意:该方法的参数是数组,如果类之间有关系的话,最好一起更新(原子方式更新)。- 抛出:
ClassNotFoundExceptionUnmodifiableClassException
-
appendToBootstrapClassLoaderSearch
添加指定jar包到根类加载器路径中- 参数:
jarfile- 要加载的jar包
-
appendToSystemClassLoaderSearch
添加指定jar包到系统类加载器路径中- 参数:
jarfile- 要加载的jar包
-