一、背景

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. 什么是线程池(thread pool)?

    在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内 存资源或者其它更多资源.在 Java 中更是如此,虚拟机将试图跟踪每一个对象, 以便能够在对象销毁后进行垃圾回收.所以提高服务程 ...

  2. Elasticsearch 在部署时,对 Linux 的设置有哪些优化方法?

    1.64 GB 内存的机器是非常理想的, 但是 32 GB 和 16 GB 机器也是很常见的.少于 8 GB 会适得其反. 2.如果你要在更快的 CPUs 和更多的核心之间选择,选择更多的核心更好.多 ...

  3. java中的generate

    流generate(Supplier s)返回无限顺序无序流,其中每个元素由提供的供应商生成.这适用于生成恒定流,随机元素流等. public class Flow { public static v ...

  4. Zookeeper 的 java 客户端都有哪些?

    java 客户端:zk 自带的 zkclient 及 Apache 开源的 Curator.

  5. AQS 支持两种同步方式?

    1.独占式 2.共享式 这样方便使用者实现不同类型的同步组件,独占式如 ReentrantLock,共享式如 Semaphore,CountDownLatch,组合式的如 ReentrantReadW ...

  6. 使用滑模控制对sin(t)曲线追踪

    结合:[Matlab]简单的滑模控制程序及Simulink仿真本片文章观看,此篇文章是在这篇文章的基础上进行修改的 输出u的推导过程 如果不明白控制量输出u的推到过成请看:[控制理论]滑模控制最强解析 ...

  7. PokemonGo:LBS游戏开发

    写在前面 去吧!皮卡丘!小时候拥有一台任天堂是多少熊孩子的梦想,每个夜晚被窝里透出的微弱光线,把小小的童年带入另一个世界,家门口的鸟和狗,森林里的虫和瀑布,山洞里的超音蝠,带着小小的梦,走过一个个城市 ...

  8. 使用Canvas和JavaScript做一个画板

    本文同步于个人博客:https://zhoushuo.me/blog/2018/03/11/drawing-borad/ 前些天学习了HTML5的<canvas>元素,今天就来实践一下,用 ...

  9. 【MarkDown】使用

    1. 插入代码 在代码前后加上三个点样式的标识符: ``` 效果: 我是代码 1-1 换行 &符号加上 "ensp;"   2. 标题样式定制 # 一级标题 ## 二级标题 ...

  10. css让文字显示特定行数,多余的显示省略号

    /*css*/ .p{ width: 200px; word-break: break-all; text-overflow: ellipsis; display: -webkit-box; /** ...