一、背景

TBS(腾讯浏览服务)是腾讯提供的移动端webview体验的整套解决方案(https://x5.tencent.com/docs/index.html),可以用于移动端加载doc、xls、pdf等文档,实现文档浏览加载服务。

Android端详细使用方法可参考:https://blog.csdn.net/czj1998/article/details/122494381

使用TbsReaderView 的openFile方法加载文件;

在关闭页面前执行TbsReaderView 的onStop方法执行内存清理,防止下次加载出现异常。

二、现象

结束文件浏览,执行TbsReaderView 的onStop方法后,发现浏览文件的Activity发生泄露,内存无法释放。

三、原因

(1)内存泄露发生在tbs sdk内部,从网络加载到本地jar包
         具体包为:data\data\包名\app_tbs_64\core_share\tbs_jars_fusion_dex.jar
(2)TBS使用dex加载器加载了本地jar包的方法,发生泄露的地方具体为com.tencent.tbs.log.TBSLog类的静态成员变量 List<Abstracte> c
         而且,每次使用 DexLoader都会再次加载,持有一个新的对象,在退出时虽然提供onStop方法将此列表置空,但是列表原持有的对象持有了Activity,导致无指针指向,内存泄露。
(3)为什么不在最开始传入ApplicationContext呢?因为这个context最终是由TbsReaderView类的openFile方法传入的,TbsReaderView类涉及UI绘制,要求必须传入Activity。进一步的,通过CLassLoader加载jar包方法,最终通过DexClassLoader传给了TBSLog类的内存泄露静态变量。

四、解决方法

(1)获取TBSLog的静态成员
(2)在执行它本身的清理方法之前,通过静态成员反射到内存泄露的类
(3)将持有的Activity置空,避免变成无指向的对象

五、代码

/**
* tbs 存在内存泄露
* 发生在tbs sdk内部,而且是tbs从网络加载到本地jar包
* 存放在:data\data\包名\app_tbs_64\core_share\tbs_jars_fusion_dex.jar
* 然后使用dex加载器加载的方法
* 具体为com.tencent.tbs.log.TBSLog类
* 静态成员 List<Abstracte> c
* 而且,每次使用 DexLoader都会再次加载,持有一个新的对象
* 在退出时虽然提供onStop方法将此列表置空
* 但是列表原持有的对象持有了Activity,导致无指针指向,内存泄露。
* ?为什么不在最开始传入ApplicationContext呢
* 因为这个context最终是由TbsReaderView类的openFile方法传入的
* TbsReaderView类涉及UI绘制,要求必须传入Activity
* 再进一步的最终通过DexClassLoader传给了TBSLog类
* <p>
* 解决思路:获取TBSLog的静态成员
* 在执行它本身的清理方法之前
* 通过静态成员反射到内存泄露的类
* 将持有的Activity置空
* 避免变成无指向的对象
*/
private void clearTbsLogContext() {
try {
DexLoader dexLoader = getTbsDexLoader(); Class<?> tbsLogClass = dexLoader.loadClass("com.tencent.tbs.log.TBSLog");
Field[] fields = tbsLogClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
//找静态对象
if (Modifier.isStatic(field.getModifiers())) {
//是私有变量
if (Modifier.isPrivate(field.getModifiers())) {
field.setAccessible(true);
Object fieldObject = field.get(null);
//不直接通过成员名反射获取对象,是因为
//tbs的包使用了混淆
//不能保证下载的包的对象名是一致的
//使用对象类型来尝试匹配吧
if (fieldObject instanceof List) {
List<Object> objectList = (List) fieldObject;
for (int j = 0; j < objectList.size(); j++) {
Object leakObject = objectList.get(j);
if (leakObject != null) {
Field[] leakFields = leakObject.getClass().getDeclaredFields();
for (int m = 0; m < leakFields.length; m++) {
Field leakField = leakFields[m];
//找到持有的context
if (leakField.getType().equals(Context.class)) {
leakField.setAccessible(true);
//释放持有的context
leakField.set(leakObject, null);
leakField.setAccessible(false);
}
}
}
}
}
field.setAccessible(false);
}
}
} } catch (Exception e) {
e.printStackTrace();
}
} /**
* tbsSDK提供了DexLoader对象来加载它下载的包
* 通过反射直接获取它生成的对象吧
* 避免还要根据路径、包名来加载dex或jar
*
* @return DexLoader
*/
private DexLoader getTbsDexLoader() {
DexLoader dexLoader = null;
try {
Field[] fields = mTbsReaderView.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.getType().equals(ReaderWizard.class)) {
field.setAccessible(true);
ReaderWizard wizard = (ReaderWizard) field.get(getTbsReaderView());
Field[] wizardFields = wizard.getClass().getDeclaredFields();
for (Field wizardField : wizardFields) {
//匹配到TbsReaderView对象已生成的DexLoader
if (wizardField.getType().equals(DexLoader.class)) {
wizardField.setAccessible(true);
dexLoader = (DexLoader) wizardField.get(wizard);
wizardField.setAccessible(false);
break;
}
}
field.setAccessible(false);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} return dexLoader;
}

腾讯tbs 内存泄露的更多相关文章

  1. JVM内存管理概述与android内存泄露分析

    一.内存划分 将内存划分为六大部分,分别是PC寄存器.JAVA虚拟机栈.JAVA堆.方法区.运行时常量池以及本地方法栈. 1.PC寄存器(线程独有):全称是程序计数寄存器,它记载着每一个线程当前运行的 ...

  2. 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理

    [微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...

  3. 关于 Unity 项目中的 Mono 堆内存泄露

    关于 Unity 项目中的 Mono 堆内存泄露 题记:这是补一篇应该在将近一年前就应该写的记录,今天终于补上. 内存泄露是一个老话题了,之前我专门写过一篇 排查 Lua 虚拟机内存泄露 的文章,并且 ...

  4. (转)专项:Android 内存泄露实践分析

    今天看到一篇关于Android 内存泄露实践分析的文章,感觉不错,讲的还算详细,mark到这里. 原文发表于:Testerhome: 作者:ycwdaaaa ;  原文链接:https://teste ...

  5. Android内存泄露调试

    Android 内存泄漏调试 一.概述 如果我们编写的代码当中有太多的对内存使用不当的地方,难免会使得我们的设备运行缓慢,甚至是死机.为了能够使得 Android 应用程序安全且快速的运行, Andr ...

  6. 问题查询-tomcat内存泄露

    1.报警信息 内容: 微信服务器向公众号推送消息或事件后,开发者5秒内没有返回 次数: 5分钟 239次 错误样例: [OpenID=o][Stamp=1562718361][3rdUrl=url][ ...

  7. Android 开发学习进程0.28 腾讯TBS接入和相关问题

    TBS 的接入和使用 TBS 的接入 腾讯TBS是X5内核的升级版,可以当作webview 来打开 网页,可以以用来打开docx doc pdf 等文件,这里主要使用的是文件功能. 依赖接入 api ...

  8. java: web应用中不经意的内存泄露

    前面有一篇讲解如何在spring mvc web应用中一启动就执行某些逻辑,今天无意发现如果使用不当,很容易引起内存泄露,测试代码如下: 1.定义一个类App package com.cnblogs. ...

  9. 查看w3wp进程占用的内存及.NET内存泄露,死锁分析

    一 基础知识 在分析之前,先上一张图: 从上面可以看到,这个w3wp进程占用了376M内存,启动了54个线程. 在使用windbg查看之前,看到的进程含有 *32 字样,意思是在64位机器上已32位方 ...

随机推荐

  1. 在 Java 中,如何跳出当前的多重嵌套循环?

    在最外层循环前加一个标记如 A,然后用 break A;可以跳出多重循环.(Java 中 支持带标签的 break 和 continue 语句,作用有点类似于 C 和 C++中的 goto 语 句,但 ...

  2. Linux文件管理 | Liunx 常用命令

    文件与目录基本操作 目录: 一.显示文件内容 cat 命令 more 命令 less 命令 head 命令 tail 命令 二.文件内容查询(grep) 三.文件查找命令 find 命令 locate ...

  3. MCU选型

    含义: MCU(Micro Controller Unit)中文名称为微控制单元,又称单片微型计算机(Single Chip Microcomputer),是指随着大规模集成电路的出现及其发展,将计算 ...

  4. 通读Python官方文档之wsgiref(未完成)

    wsgirf-WSGI功能及参考实现 源码:Lib/wsgiref Web服务器网关接口(Web Server Gateway Interface, WSGI),是用Python写的一个服务器软件和w ...

  5. VISIO下载+安装+第一个数据流图

    一. 下载地址 Visio2021 (64bit).zip_免费高速下载|百度网盘-分享无限制 (baidu.com) 码3333 二. 安装步骤 Visio2021安装教程 (qq.com) 三. ...

  6. PAT 1048数字加密

    本题要求实现一种数字加密方法.首先固定一个加密用正整数 A,对任一正整数 B,将其每 1 位数字与 A 的对应位置上的数字进行以下运算:对奇数位,对应位的数字相加后对 13 取余--这里用 J 代表 ...

  7. Android修改app图标

    1.按照路径找到AndroidManifest.xml中的icon 2.在drawable添加一个png图片 3.然后在AndroidManifest.xml中的icon,修改其中的值 android ...

  8. Redis快速入门到精通

    Redis Redis是一个开源的使用ANSI C语言编写.支持网络. 可基于内存亦可持久化的日志型.Key-Value型 NoSQL数据库,并提供多种语言的API.从2010年3 月15日起,Red ...

  9. 【论文阅读】ICLR 2022: Scene Transformer: A unified architecture for predicting future trajectories of multiple agents

    ICLR 2022: Scene Transformer: A unified architecture for predicting future trajectories of multiple ...

  10. 01 | 堆、栈、RAII:C++里该如何管理资源?(极客时间笔记)

    基本概念 堆,英文是 heap,在内存管理的语境下,指的是动态分配内存的区域.这个堆跟数据结构里的堆不是一回事.这里的内存,被分配之后需要手工释放,否则,就会造成内存泄漏. C++ 标准里一个相关概念 ...