使用FragmentStatePagerAdapter时发现的内存泄露问题

这篇文章想说的并非是由于使用 FragmentStatePagerAdapter 而导致的内存泄漏,内存泄漏的真正原因和 FragmentStaePagerAdapter 并无直接关联,但是使用 FragemntStatePagerAdapter 能让你更加直观的发现内存泄漏。

我先说说我之前遇到的问题。我们都知道,当使用 FragmentSataePagerAdapter 的时候,超出缓存范围的 Fragment 会被 FragmentManager 给 remove 掉,也就是会被销毁实例,所以FragmentSatatePagerAdapter 很适合用来处理多 Fragment 页面的状况。但我遇到的情况是,当我的 Fragment 超出缓存范围时,确实被销毁了,但是它所持有的内存却并未得到释放,也就是说它内部的数据等等并未销毁和回收。如果是页面很多的情况下,很容易就会造成程序的卡顿甚至 OOM。

一般来说,造成内存泄漏的最大可能就是某处持有该对象的引用,导致该对象无法释放内存。经排查,我在使用 ViewPager 时,创建了一个 Fragment 的 List ,用来管理所有要添加到 ViewPager 中的 Fragment ,这里的 List 可能强引用了所有的Fragment,所以造成了内存泄漏 。代码如下:

private List<Fragment> fragmentList = new ArrayList<>();  //创建List,用来管理所有要添加到ViewPager的Fragment

//添加Fragment
private void setUpFragments() {
    fragmentList.clear();
    fragmentList.add(new SampleListFragment());
    fragmentList.add(new SamplePagerFragment());
    fragmentList.add(new BlankFragment());
}

viewPager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) {
        @Override
        public Fragment getItem(int position) {
            return fragmentList.get(position);  //从Fragment队列中得到Fragment并加入到ViewPager中
        }

        @Override
        public int getCount() {
           return fragmentNames.size();
        }
    });

那么如何来验证是不是这里的原因造成内存泄漏呢?想要证明,首先就不能够在使用 List 来管理 Fragment对象了。ViewPager最麻烦的是你不能自己使用 FragmentManager 的transaction 来添加 Fragment ,因为这些操作是在 ViewPager 的内部去完成的。最后我考虑使用 List 来管理Fragment的CLASS 类,然后再通过反射的方式,创建出 Fragment 对象。这样 Fragment 对象就不会被 List 持有引用了。具体代码如下:

private List<Class> fragmentNames;   //创建List来管理 Fragment的 Class

//添加Fragment的Class到List中
private void setUpFragments() {
        if(fragmentNames  == null){
            fragmentNames = new ArrayList<>();
        }
        fragmentNames.clear();
        fragmentNames.add(SampleListFragment.class);
        fragmentNames.add(SampleListFragment.class);
        fragmentNames.add(SampleListFragment.class);
        fragmentNames.add(SampleListFragment.class);
    }

 viewPager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                try {
                    return (Fragment) fragmentNames.get(position).newInstance();      //反射加载Fragment
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public int getCount() {
                return fragmentNames.size();
            }
        });

如果不会反射的同学可以看一下我前面写的介绍反射的简单使用的文章。

现在再来检查程序的内存状况,你就会发现内存能够正确的被释放,内存泄漏的问题也就解决了。

其实我以前使用 ViewPager + Fragment 的时候也并没有注意到内存泄漏的问题,直到使用了FragmentStatePagerAdapter 时才发现原来的写法可能会造成内存泄漏。当然,程序中还会有很多地方稍不注意就会导致内存泄漏的问题,我们平时写代码时应该多多考虑内存泄漏的问题,并对此进行优化。

时间: 2024-10-29 19:06:02

使用FragmentStatePagerAdapter时发现的内存泄露问题的相关文章

go内存泄露case

用go写了一个守护进程程序:用于检测redis的存活状态并将结果写到zookeeper中,部署到redis机器上,对于每个redis实例会有一个goroutine每隔固定时间去检测其状态,由主goroutine负责信号处理等,再接收到信号时kill其他的goroutine.程序运行了一段时间发现,有些redis实例的对应zookeeper的信息不更新,通过日志发现对应redis的goroutine挂掉了.阅读源码发现貌似是zk的第三方库抛出一个非预期的异常导致. 为了解决这个问题,对逻辑重构:由

FastMM内存泄露

转自:http://www.2ccc.com/article.asp?articleid=4879FastMM是非常优秀的内存管理器,但是从FastMM4Options.inc中找到合适自已程序的选项设置免不了一顿英文的纠缠,所以我把这个文件里的所有英文注释都翻译成了中文.如果你发现有任何翻译不恰当的地方,直接进行修改就可以了.呵呵,给FastMM作者写了封邮件,这个文件也会被包含在下一个FastMM版本中.如果你不愿意下载,直接把下面内容复制到FastMM4Options.inc文件里并覆盖原

BingMap频繁Add Pushpin和Delete Pushpin会导致内存泄露

最近在做性能测试的时候发现BingMap内存泄露(memory leak)的问题,查找了一些国外的帖子,发现也有类似的问题,但是没有好的解决办法. https://social.msdn.microsoft.com/Forums/en-US/3226f255-2ae1-4718-b848-5f24e76b64b0/your-pushpins-are-broken-addremove-leads-to-memory-leak?forum=bingmapsajax 经过一番尝试,找到了一个折中的解决

c代码连接mysql数据库内存泄露的问题

一直使用C代码连接mysql数据库,今天用valgrind检测,发现存在内存泄露的问题 代码如下 MYSQL* connection; connection = mysql_init(); connection = mysql_real_connect(connection,......); mysql_query(.........); mysql_close(connection); 在网上查了一番,找到如下解决方案 http://pipal.iteye.com/blog/903506 在m

Android性能优化:手把手带你全面了解 内存泄露 &amp; 解决方案

. 简介 即 ML (Memory Leak)指 程序在申请内存后,当该内存不需再使用 但 却无法被释放 & 归还给 程序的现象2. 对应用程序的影响 容易使得应用程序发生内存溢出,即 OOM 内存溢出 简介: 示意图3. 发生内存泄露的本质原因 具体描述示意图 特别注意 从机制上的角度来说,由于 Java存在垃圾回收机制(GC),理应不存在内存泄露:出现内存泄露的原因仅仅是外部人为原因 = 无意识地持有对象引用,使得 持有引用者的生命周期 > 被引用者的生命周期4. 储备知识:Androi

Android性能优化:阿里、腾讯等关于内存泄露的知识都在这里了!

建议收藏,不然就找不到了!!! 前言 在 Android 中,内存泄露的现象十分常见:而内存泄露导致的后果会使得应用Crash本文 全面介绍了内存泄露的本质.原因 & 解决方案,最终提供一些常见的内存泄露分析工具,希望你们会喜欢. 目录 1. 简介 即 ML (Memory Leak)指 程序在申请内存后,当该内存不需再使用 但 却无法被释放 & 归还给 程序的现象 2. 对应用程序的影响 容易使得应用程序发生内存溢出,即 OOM内存溢出 简介: 3. 发生内存泄露的本质原因 具体描述 特

Mysql: Connect/C++ 使用过程中发现返回 std::string 造成的内存泄露

在使用 Connect/C++ ,测试时发现在调用 getString 出现了内存增长的情况. ConstructOutput(); //打印出当前内存 for(int i=0;i<1000;++i) { prepareState.reset(con->prepareStatement("call test.testproc3(?)")); prepareState->setInt(1,1001); prepareState->executeUpdate();

使用jprofile发现和修复内存泄露

性能分析有一项是:发生OOM时,浏览对象分配和引用以发现和修复内存泄露: 示例程序PointFactory public class PointFactory { protected ArrayList points = new ArrayList(); protected static PointFactory instance = new PointFactory(); public Point createPoint(int x, int y) { Point point = new Po

Object-C使用类静态方法创建对象时容易内存泄露

1.所谓使用类的静态方法创建对象,就是指使用类名调用一次它的静态方法(非显式调用alloc)便可以得到一个新建的对象,比如下面两个例子: NSString* str1 = [NSString stringWithString:@"hello world"]; NSMutableString* str2 = [NSMutableString stringWithString:@"hello world"]; 2. 第一个例子是使用字符串的字面常量"hello