腾讯tbs 内存泄露
一、背景
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 内存泄露的更多相关文章
- JVM内存管理概述与android内存泄露分析
一.内存划分 将内存划分为六大部分,分别是PC寄存器.JAVA虚拟机栈.JAVA堆.方法区.运行时常量池以及本地方法栈. 1.PC寄存器(线程独有):全称是程序计数寄存器,它记载着每一个线程当前运行的 ...
- 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理
[微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...
- 关于 Unity 项目中的 Mono 堆内存泄露
关于 Unity 项目中的 Mono 堆内存泄露 题记:这是补一篇应该在将近一年前就应该写的记录,今天终于补上. 内存泄露是一个老话题了,之前我专门写过一篇 排查 Lua 虚拟机内存泄露 的文章,并且 ...
- (转)专项:Android 内存泄露实践分析
今天看到一篇关于Android 内存泄露实践分析的文章,感觉不错,讲的还算详细,mark到这里. 原文发表于:Testerhome: 作者:ycwdaaaa ; 原文链接:https://teste ...
- Android内存泄露调试
Android 内存泄漏调试 一.概述 如果我们编写的代码当中有太多的对内存使用不当的地方,难免会使得我们的设备运行缓慢,甚至是死机.为了能够使得 Android 应用程序安全且快速的运行, Andr ...
- 问题查询-tomcat内存泄露
1.报警信息 内容: 微信服务器向公众号推送消息或事件后,开发者5秒内没有返回 次数: 5分钟 239次 错误样例: [OpenID=o][Stamp=1562718361][3rdUrl=url][ ...
- Android 开发学习进程0.28 腾讯TBS接入和相关问题
TBS 的接入和使用 TBS 的接入 腾讯TBS是X5内核的升级版,可以当作webview 来打开 网页,可以以用来打开docx doc pdf 等文件,这里主要使用的是文件功能. 依赖接入 api ...
- java: web应用中不经意的内存泄露
前面有一篇讲解如何在spring mvc web应用中一启动就执行某些逻辑,今天无意发现如果使用不当,很容易引起内存泄露,测试代码如下: 1.定义一个类App package com.cnblogs. ...
- 查看w3wp进程占用的内存及.NET内存泄露,死锁分析
一 基础知识 在分析之前,先上一张图: 从上面可以看到,这个w3wp进程占用了376M内存,启动了54个线程. 在使用windbg查看之前,看到的进程含有 *32 字样,意思是在64位机器上已32位方 ...
随机推荐
- java中的函数式接口
是什么?? 有且只有一个抽象方法的接口 场景: 适用于函数式编程场景(使用lambda表达式编程)的接口,函数式接口可以适用于lambda使用的接口. 只有确保接口中有且只有一个抽象方法,java中的 ...
- 什么是持续集成CI?
持续集成(CI)是每次团队成员提交版本控制更改时自动构建和测试代码的过程. 这鼓励开发人员通过在每个小任务完成后将更改合并到共享版本控制存储库来共 享代码和单元测试.
- vue中v-model 数据双向绑定
表单输入绑定 v-model 数据双向绑定,只能应用在input /textare /select <div id="app"> <input type=&quo ...
- 单例模式 | C++ | Singleton模式
Singleton 模式 单例模式(Singleton Pattern)是 C++/Java等语言中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式实 ...
- 顺利通过EMC实验(14)
- html 元素 强制不换行
html 中 nowrap是用来强制不换行的 在排版中 对包裹plain text的标签使用nowrap属性即刻实现强制不换行. 如:<p nowrap>强制不换行</p>&l ...
- 微信小程序版博客——开发汇总总结(附源码)
花了点时间陆陆续续,拼拼凑凑将我的小程序版博客搭建完了,这里做个简单的分享和总结. 整体效果 对于博客来说功能页面不是很多,且有些限制于后端服务(基于ghost博客提供的服务),相关样式可以参考截图或 ...
- java中匿名内部类的匿名构造函数是怎么用的
java中匿名内部类的匿名构造函数是怎么用的下面的例子说明匿名内部类的匿名构造函数的用法 例2.7.2_0interface FigureMark_to_win { void whoAmI(); ...
- PAT B1056组合数的和
给定 N 个非 0 的个位数字,用其中任意 2 个数字都可以组合成 1 个 2 位的数字.要求所有可能组合出来的 2 位数字的和.例如给定 2.5.8,则可以组合出:25.28.52.58.82.85 ...
- 小程序获取自定义属性之e.target与e.currentTarget
彻底弄懂小程序e.target与e.currentTarget 一.小程序中关于事件对象 e 的属性中有两个特别重要的属性:target与currentTarget属性:对于这两个属性,官方文档上 ...