原因调查

因为每个方法都被插桩, 所以每个方法的前后调用关系都做成MethodNode被存到了堆栈中,

所以随着使用时间的增长, 所有方法关系都存进了堆栈中, 不被清理, 就撑爆了内存.

设计方案

这个问题实际上是个OOM问题. OOM从技术角度看, 是因为改释放的内存不被释放.

但是要解决OOM问题, 却需要从具体业务入手, 因为只有通过业务关系梳理, 才知道那些内存应该释放.

针对这次的问题, 就是要减少方法关系的存储.

所以做如下设计:

过滤掉重复的方法调用关系MethodNode. 只保存精简方法关系堆栈

制作MethodNode缓存池, 创建MethodNode时, 优先从缓存池中获取. 获取失败再创建

每个被配判定为重复的方法对象MethodNode, 存入缓存池等待复用

方案设计好, 又面临如下问题:

如何判断"重复的调用关系"?

缓存池要频繁读写, 如何设计避免读写冲突?

1. 重复调用关系判断

因为MethodNode中保存了父节点和子节点信息. 所以当得到一个新MethodNode时,

获取它的父节点信息, 再从现有的堆栈中, 找到对应的父节点,

查看该父节点中的所有子节点是否已经包含那个新MethodNode的信息.

如果包含, 说明重复, 则丢弃这个新MethodNode. 如果不包含, 就把新MethodNode加入堆栈里父节点的子节点列表里,

如果没有找到父节点, 则这个新MethodNode直接当做root级别父节点, 存入堆栈

2. 缓存池设计

聊这个, 我可就不困了.

特别为这个设计里一个存取交换池.

简单的说, 设计了两个缓存池, 某个时间段内, 一个池只取, 另一个池只存.

当"只取"的池子空了, 就切换成"只存"状态. 同时, 之前"只存"的池子同步切换为"只取"

这样两个池子, 交替使用

如果两个坑都没有砖, 我就去找砖厂要砖. 回收的砖同样按照轮换扔到当前应该回收转的坑里.

相当于做到存取隔离, 也就没有了存取冲突. 同时有复用机制. 内存的数量不会激增.

过程大致如下:

Android统计方法耗时之:内存控制_内存管理

结果验证

按照设计, 开发完毕实测.

缓存池初期不断增大, 2w个之后逐渐稳定.不再增长.

满足了整个app的循环复用. 内存问题解决.

​​