Java热更新


一、  解决方案

1)  自定义类加载器。

首先需要明白一点,class相等的判断条件不仅仅是类名相同,还需要加载它的ClassLoader相同。JVM内部规定一个ClassLoader不可以重复定义类,也就是说想要重定义一个类,就必须使用一个全新的ClassLoader。

JVM内部class被卸载的条件及其苛刻,甚至没有明确的方法可以直接调用,只有当加载该类型的类加载器实例为unreachable状态时,也就是没有任何实例,class才有可能被卸载。(启动类加载器实例永远为reachable状态,由启动类加载器加载的类型可能永远不会被卸载)

<span style="white-space:pre">	</span>public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
		Class<?> clazz = null;
		// 首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,如果已经装载,直接返回;否则继续。
		if (name.startsWith("com.wafer") || name.contains("Service")) {
			if (resolve) {
				resolveClass(clazz); // 链接指定的 Java 类
			}
			// 如果class类被修改过,则重新加载
			MoeLoader hcl = new MoeLoader(url);
			clazz = customLoad(name, hcl);
			return (clazz);
		}
		// 如果类的包名为"java."开始,则有系统默认加载器加载
		try {
			// 得到系统默认的加载cl
			ClassLoader system = ClassLoader.getSystemClassLoader();
			clazz = system.loadClass(name); // 加载名称为 name的类
			if (clazz != null) {
				if (resolve)
					resolveClass(clazz);
				return (clazz);
			}
		} catch (ClassNotFoundException e) {
			// Ignore
		}
		return customLoad(name, this);
	}


此范例的核心在于缓存自己已经加载的class,当再次需要加载时,如果发生变更,则可以new一个ClassLoader,这样新的字节码便可以即时生效。

JRbel是一种热更新的方案,它实现的方式是通过在启动参数中添加javaagent,即JVM底层提供的Instrumentation技术,来改变生成对象的方式。

2)  JVMTI虚拟机工具接口

JPDA是 Java 平台调试体系结构的缩写。通过 JPDA 提供的 API,开发人员可以方便灵活的搭建 Java 调试应用程序。 JPDA 主要由三个部分组成:Java 虚拟机工具接口(JVMTI)、Java 调试线协议(JDWP),以及 Java 调试接口(JDI)。参考资源(http://www.ibm.com/developerworks/cn/views/java/libraryview.jsp?search_by=深入+Java+调试体系)

<span style="white-space:pre">	</span>List<Connector> connectors =    Bootstrap.virtualMachineManager().allConnectors();
		SocketAttachingConnector sac = null;
		for (Connector connector : connectors) {
			if (connector instanceof SocketAttachingConnector) {
				sac = (SocketAttachingConnector) connector;
			}
		}
		if (sac != null) {
			Map<String, Connector.Argument> defaultArguments = sac.defaultArguments();
			Connector.Argument hostArg = defaultArguments.get("hostname");
			Connector.Argument portArg = defaultArguments.get("port");
			hostArg.setValue("localhost");
			portArg.setValue("8787");
			VirtualMachine vm = sac.attach(defaultArguments);

			List<ReferenceType> rtList = vm.classesByName("com.wafer.demo.jdi.Foo");
			ReferenceType rt = rtList.get(0);
			Map<ReferenceType, byte[]> newByteCodeMap = new HashMap<ReferenceType, byte[]>(1);
			//获取特定类的字节码,发送到虚拟机
			byte[] newByteCode = genNewByteCodeUsingJavassist();
			newByteCodeMap.put(rt, newByteCode);

			if (vm.canRedefineClasses()) {
				vm.redefineClasses(newByteCodeMap);
			}
		}


Eclipse的热更新实现的方式便是如此,以Debug启动时,Eclipse会在JVM参数中添加-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:63597,之后检测到代码变更时,将变更的字节码通过jdwp发送到应用。

BTrace可以发送字节码到应用,对拦截的类进行监控,其实现的方式同样用到JVMTI。使用VirtualMachine.attach(pid)连接到应用。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java Agent是一种Java技术,它可以在程序运行时动态地修改或增强现有的Java类。Java Agent可以用于很多场景,其中之一就是实现Java应用程序的热更新。下面是一个简单的Java Agent热更新的示例: 1.编写Java Agent程序,实现热更新功能。具体实现可以使用Java工具包tools.jar提供的agentmain方法,该方法可以在程序运行时动态地修改或增强现有的Java类。以下是一个简单的Java Agent程序示例: ```java public class HotUpdateAgent { public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { // 获取要更新的类的类名 String className = "com.example.demo.HelloWorld"; // 获取要更新的类的字节码文件 byte[] classBytes = getClassBytes(className); // 重新定义要更新的类 ClassDefinition classDefinition = new ClassDefinition(Class.forName(className), classBytes); inst.redefineClasses(classDefinition); } private static byte[] getClassBytes(String className) throws Exception { // 读取要更新的类的字节码文件 FileInputStream fis = new FileInputStream(className.replaceAll("\\.", "/") + ".class"); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { bos.write(buffer, 0, len); } fis.close(); bos.close(); return bos.toByteArray(); } } ``` 2.在需要热更新Java应用程序中,使用Java Agent实现热更新。具体实现可以使用Java命令的-javaagent参数来指定Java Agent程序,并使用HotUpdateServer类来启动Java应用程序。以下是一个简单的Java应用程序示例: ```java public class HelloWorld { public static void main(String[] args) throws Exception { while (true) { System.out.println("Hello World!"); Thread.sleep(1000); } } } ``` 3.使用HotUpdateServer类来启动Java应用程序,并使用Java命令的-javaagent参数来指定Java Agent程序。以下是一个简单的HotUpdateServer类示例: ```java public class HotUpdateServer { public static void main(String[] args) throws Exception { // 获取要更新的Java应用程序的pid String pid = args[0]; // 获取要更新的类的项目目录和类名 String classPath = args[1]; // 获取执行引擎jar的路径 String enginePath = args[2]; // 启动Java应用程序 Process process = Runtime.getRuntime().exec("java -javaagent:" + enginePath + " -cp " + classPath + " HelloWorld"); // 获取Java应用程序的输入流和输出流 InputStream inputStream = process.getInputStream(); OutputStream outputStream = process.getOutputStream(); // 启动热更新线程 new Thread(() -> { while (true) { try { // 等待用户输入命令 Scanner scanner = new Scanner(System.in); String command = scanner.nextLine(); // 发送命令到Java应用程序 outputStream.write((command + "\n").getBytes()); outputStream.flush(); // 读取Java应用程序的输出 byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) != -1) { System.out.write(buffer, 0, len); } } catch (Exception e) { e.printStackTrace(); } } }).start(); // 等待Java应用程序退出 process.waitFor(); } } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值