一、首先需要了解java的内存管理机制,大致分为3类

  • 方法区
  • 堆区
  • 栈区

三种内存区域定义如下:

方法区:当JVM使用类加载器定位class文件,并将其输入到内存中时,会提取class文件的类型信息,并将这些信息存储到方法区中。同时放入方法区中的还有该类型中的类静态变量。

堆内存:java程序在运行时创建的所有类型对象和数组都存储在堆中。JVM会根据new指令在堆中开辟一个确定类型的对象内存空间。但是堆中开辟对象的控件并没有任何人工指令可以回收,而是通过JVM的垃圾回收期负责回收。

Java栈:每启动一个线程(main),JVM都会为他分配一个java栈,用于存放方法中的局部变量,操作数以及异常数据等,当线程调用某个方法时,JVM会根据方法区中该方法的字节码组建一个栈帧,并将该栈帧压入java栈中,方法执行完毕时,JVM会弹出该栈帧并释放掉。所以,我们app应用被压入的第一个栈帧就是ActiviytThread类的main()方法。当main被释放掉时,我们的app就退出了。

本地方法栈:native堆

二、内存运行机制

类在没加载之前会在方法区声明一个int类型符号变量,它指向即将要加载内存的class类信息。当前类的字节码文件中的类信息,包括方法,成员变量。虚拟机加载的时候不会原封不动的把字节码信息加载到内存,而是会做一些调整,其中一点就会创建一个方法表(方法表实际上在native层是ArtMethod数组,方法表中的每一个方法都是ArtMethod结构体),这个方法表包含了这个类的所有方法(字节码转内存过程)。

类是从本地apk文件加载的,当我们安装一个apk时,会拷贝一份apk文件到系统data/app/xxxde1目录下,启动app应用就会通过dexfile读取第一个文件ActivityThread字节码文件,找到可执行的方法main,然后从类的方法表中将main方法压栈,形成一个栈帧。在ActivityThread类中会声明一个app加载的第二个类是application类。执行流程一样,会将onCreate方法压入栈,形成栈帧。所以方法的执行时通过压栈的方式执行的,方法的执行也是需要内存的(方法的执行最终是转为ArtMethod结构体去执行的)。伪代码如下:

class ActivityThread{Application application;public static void main(string[] args){application = new Application();application.onCreate(...);}}

在ActivityThread类中声明了Application 类,此时类是没有被加载到内存的,只有在主动引用的时候才会被加载到内存(new操作符、反射class.forName、jni.findClass等)。执行到这里只会在方法区创建一个int类型符号变量。当执行到new的时候,会从我们的apk文件中找到字节码文件,在方法区为它开辟一个存储空间(创建方法表),并且当前的符号变量指向它。此时,class类还没有加载完成,同时在堆区开辟另外一块内存,存放对象的变量信息,这个对象会指向符号变量(实际是通过klass指向它,klass是类的载体,是唯一的)。这就是通过对象能够找到类的原因,伪代码如下:

Student stu = new Student();
Class clazz =stu.getClass();//通过对象找到类

对象内存分配完毕,此时执行到对象的onCreate方法,然后对象会送一个事件通过符号变量找到方法表中的该方法,并将它压栈,形成一个onCreate栈帧。

三、热修复分类:两大类

  • native层:andfix、sophix
  • java层:tinker、robsut

热升级:增量升级

两者异同点:都需要旧的apk与新的apk进行比对,生成差分包。实现的方式是一样的。不同点,目的性不一样

热修复是针对bug,热升级是根据版本升级。

四、AndFix

andFix是通过替换方法表中的方法实现热修复的,是native层的实现

五、手写AndFix修复

1、模拟一个bug类,其中有一个方法抛异常,代码如下:

package com.xinyartech.andfix.bug;
/*** <pre>*     desc    : bug类* </pre>*/
public class AndFixTool {public int testMethod(){throw new RuntimeException("报异常了,需要修复");}
}

2、定义注解类,标明需要修复的类及方法,代码如下:

package com.xinyartech.andfix;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** <pre>*     desc    : 注解类 获取需要修复的类及方法* </pre>*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FixAnnotation {String clazz();//修复哪一个类String method();//替换哪一个方法
}

3、模拟一个修复类,修复该方法,在修复方法上加入修复类的全路径,及方法名,代码如下:

package com.xinyartech.andfix.fix;import android.util.Log;import com.xinyartech.andfix.FixAnnotation;/*** <pre>*     desc    : 修复类* </pre>*/
public class AndFixTool {@FixAnnotation(clazz = "com.xinyartech.andfix.bug.AndFixTool",method = "testMethod")public int testMethod(){Log.e("AndFixTool","已经修复了");return 10;}
}

4、借助android studio编译,build一下项目,生成字节码文件。将编译的字节码文件夹拷贝到桌面,备后续使用,注意:红色的是包含包名的路径

5、删除和修复包无用的字节码文件,如下:

6、使用dx.bat命令将当前修复包打成dex文件,命令如下,需要在环境变量中配置dx命令

7、执行dx生成一个dex文件

这个fix.dex文件就是我们的修复文件,一般需要我们将这个文件放到服务器,供用户下载,目前我们手动把修复包放到手机sd卡

8、在Activity中模拟修复方法,fixMethod(),代码如下:

public void fixMethod(View view) {File file = new File(Environment.getExternalStorageDirectory(), "fix.dex");DexManager dexManager = new DexManager(this);dexManager.load(file);
}

DexManager代码如下:

public class DexManager {private Context context;public DexManager(Context context) {this.context = context;}public void load(File file) {try {//通过dexFile下载dex文件,这个是fix.dexDexFile dexFile = DexFile.loadDex(file.getAbsolutePath(),new File(context.getCacheDir(), "opt").getAbsolutePath(), Context.MODE_PRIVATE);//获取当前的dex里面的class 类名集合Enumeration<String> entry=dexFile.entries();while (entry.hasMoreElements()) {String clazzName= entry.nextElement();Class realClazz= dexFile.loadClass(clazzName, context.getClassLoader());if (realClazz != null) {fixClazz(realClazz);}}} catch (IOException e) {e.printStackTrace();}}private void fixClazz(Class realClazz) {Method[] methods=realClazz.getMethods();for (Method rightMethod : methods) {Replace replace = rightMethod.getAnnotation(Replace.class);if (replace == null) {continue;}//获取要修复的类名和方法String clazzName=replace.clazz();String methodName=replace.method();try {//获取有bug的classClass wrongClazz=  Class.forName(clazzName);//获取有bug的方法Method wrongMethod = wrongClazz.getDeclaredMethod(methodName, rightMethod.getParameterTypes());//native替换方法replace(wrongMethod, rightMethod);} catch (Exception e) {e.printStackTrace();}}}//native层方法public  native static void replace(Method wrongMethod, Method rightMethod) ;
}

9、native层实现

(1)需要拷贝系统的和ArtMethod相关的两个文件,目录如下:E:\BaiduNetdiskDownload\Android系统源码\android-5.0.0_r7\android-5.0.0_r7\art\runtime\mirror。。文件名为object.h和art_method.h拷贝到cpp目录下。然后在natice-lib.cpp中添加如下代码:

#include <jni.h>
#include <string>
#include "art_method.h"//引入头文件
extern "C" JNIEXPORT jstring JNICALL
JNIEXPORT void JNICALL
Java_com_xinyartech_andfix_DexManager_replace(JNIEnv *env, jclass type, jobject wrongMethod,jobject rightMethod) {
//ArtMethod  Android 系统源码中art::mirror::ArtMethod *wrong=  (art::mirror::ArtMethod *)env->FromReflectedMethod(wrongMethod);art::mirror::ArtMethod *right=  (art::mirror::ArtMethod *)env->FromReflectedMethod(rightMethod);
//    method   --->class  --->ClassLoaderwrong->declaring_class_ = right->declaring_class_;wrong->dex_cache_resolved_methods_ = right->dex_cache_resolved_methods_;wrong->access_flags_ = right->access_flags_;wrong->dex_cache_resolved_types_ = right->dex_cache_resolved_types_;wrong->dex_code_item_offset_ = right->dex_code_item_offset_;
//    这里   方法索引的替换wrong->method_index_ = right->method_index_;wrong->dex_method_index_ = right->dex_method_index_;}

注意:这里引入的是5.0系统的.h文件,所以只适用5.0系统的热修复,这也是andfix严重缺陷。如果需要其他版本的也能够兼容,就需要对不同的系统文件进行引入

原因是这样的,每一个系统版本谷歌工程师都会对ArtMethod结构体动刀,导致有些参数的长度有变化,比如在5.0系统是32位的,在6.0可能就会被改成16位,这样改会导致我们的结构体结构会发生变化,发生溢出。但我们实际操控结构体的时候,就会发现有些变量找不到的情况。其实阿里开源的AndFix针对每个系统版本都做了兼容,不过目前只到7.0系统,后面的版本没有继续更新维护了。截图如下:

要解决所有版本的兼容问题,那么阿里推出了sophix热修复,它是在andfix的基础上解决了上面的问题,但是它是收费的,5000台手机免费。其实解决思路应该就是artmethod结构体的大小不能有变化。但是,目前不得而知。

阿里AndFix与sophix热修复原理解析相关推荐

  1. Android 热修复原理解析

    概述 关联文章 JVM 类加载机制 Android 中的ClassLoader 假如刚发布的版本出现了bug,我们就需要解决bug,并且重新发布新的版本,这样会浪费很多的人力物力,有没有一种可以不重新 ...

  2. 热修复原理学习(1)热修复技术介绍

    今天开始学习热修复的原理知识,学习方向是阿里团队编写的<深入探索Android热修复技术原理>,所以研究的热修复框架是Sophix. 之前对热修复的知识做过了解,具体是这一篇:热修复原理学 ...

  3. 阿里分布式事务框架Seata原理解析

    阿里分布式事务框架Seata原理解析 作者:伊凡的一天 链接:https://www.jianshu.com/p/044e95223a17 Seata框架是一个业务层的XA(两阶段提交)解决方案.在理 ...

  4. Android应用市场省流量更新(增量升级)原理解析

    一.前言 最近在看热修复相关的框架,之前我们已经看过了阿里的Dexposed和AndFix这两个框架了,不了解的同学可以点击这里进行查看:Dexposed框架原理解析 和 AndFix热修复框架原理解 ...

  5. 分布式架构原理解析常见问题解决

    大家觉得写还可以,可以点赞.收藏.关注一下吧! 也可以到我的个人博客参观一下,估计近几年都会一直更新!和我做个朋友吧!https://motongxue.cn 分布式架构原理解析常见问题解决 1. 分 ...

  6. RocketMQ原理解析-producer 4.发送分布式事物消息

    2019独角兽企业重金招聘Python工程师标准>>> RocketMQ原理解析-producer 4.发送分布式事物消息 博客分类: MQ 为什么消息要具备事务能力 还是比较清晰的 ...

  7. Tengine HTTPS原理解析、实践与调试【转】

    本文邀请阿里云CDN HTTPS技术专家金九,分享Tengine的一些HTTPS实践经验.内容主要有四个方面:HTTPS趋势.HTTPS基础.HTTPS实践.HTTPS调试. 一.HTTPS趋势 这一 ...

  8. ThreadLocal系列(二)-InheritableThreadLocal的使用及原理解析

    ThreadLocal系列之InheritableThreadLocal的使用及原理解析(源码基于java8) 上一篇:ThreadLocal系列(一)-ThreadLocal的使用及原理解析 下一篇 ...

  9. 网站app被劫持怎么办?HTTPDNS阿里云域名防劫持, DNSPod 移动解析服务 D+

    网站app被劫持怎么办?HTTPDNS阿里云域名防劫持, DNSPod 移动解析服务 D+ HTTPDNS_移动开发_域名解析_域名防劫持-阿里云 https://www.aliyun.com/pro ...

  10. 网站app被劫持怎么办?dns被劫持,域名被劫持,HTTPDNS阿里云域名防劫持, DNSPod移动解析防劫持服务D+...

    网站app被劫持怎么办?dns被劫持,域名被劫持 HTTPDNS阿里云域名防劫持, DNSPod移动解析防劫持服务D+ 垄断,就是鸡国的毒瘤. 域名被中移动劫持了,mlgb!! 关于互联网流量劫持分析 ...

最新文章

  1. (转)Blend操作入门: 别站在门外偷看,快进来吧!
  2. JavaScript原生对象常用方法总结
  3. 什么不是预防计算机病毒的方法,预防计算机病毒的方法是什么
  4. 20145326蔡馨熠《信息安全系统设计》第2周学习总结
  5. 《天天数学》连载44:二月十三日
  6. java分布式事务 实例_spring整合atomikos实现分布式事务的方法示例
  7. 数据可视化的十大优点
  8. 3.1 栈—栈的存储实现和运算实现
  9. 设备和驱动器中删除空白图标
  10. mysql php 3级联动_php mysql 采用ajax技术的 省 市 地 3级联动无刷新菜单 源码
  11. java流程图表示输入 输出_流程图 - 迷途行者 - 博客园
  12. 递归算法经典实例python-Python实现经典递归算法
  13. 统一建模语言UML简答题/期末考试分享
  14. java tm插件下载_Java(TM) Platform SE binary
  15. 2021-10-11 今日总结
  16. 小程序实现canvas添加图文
  17. 谷歌浏览器切换页面或者隐藏页面造成定时器延缓或者停止的问题。
  18. 旅行商问题(TSP)的两种模型
  19. Gemagic Design X快捷键命令
  20. 终端APP性能测试——内存篇

热门文章

  1. svn服务器调整显示图标修改,svn图标不显示可以通过调整Tortoise图标名称的字母顺序来解决...
  2. javascript页面刷新的几种方法
  3. PolSARpro导入外部极化矩阵数据(以高分三号为例,附格式转化代码)
  4. 第四章 MyBatis缓存和注解的使用
  5. 如何将手机哔哩哔哩缓存的m4s格式的视频转换成mp4呢?
  6. 使用js正则匹配和替换淘口令边界
  7. click事件修改css_CSS Click事件
  8. radon变换的原理-通过直线方程式的计算来检测出直线
  9. 韩立刚计算机网络笔记-第01章 计算机网络详解
  10. 浙大翁凯老师Java课堂学习记录(第三周)