美文网首页
内存泄漏的一个小思考

内存泄漏的一个小思考

作者: 长点点 | 来源:发表于2022-03-20 18:50 被阅读0次

重构了之前一个自定义view中的视图绘制代码。
业务需求是,在指定区域内绘制一排竖线,显示区域内一共51个,区域外的70个,每10个显示一个长的竖线,要求可以用手指左右滑动,所有竖线循环显示,队首队尾的竖线首位相接,类似于广告弹幕的循环方式。
之前的代码是面向过程的编码思路,思路很好,但是代码很烂,一大堆参数不知道作什么的,一大堆循环不知道运行到哪里了,bug一大堆不知道怎么调试。再此情况下,决定重构此处代码,改成面向对象编程
我的思路很简单
1.一个51长度的数组,表示需要被显示的竖线容器,在ondraw方法中,可以直接根据此数组绘制出所有竖线。一个121长度的数组,作为被复用的静态对象池。
2.一个基类Line,定义一些参数,竖线的宽度,长度,位置偏移量,颜色。两个实现内部静态类,ShortLine和LongLine
3.在view类创建时,在static块中初始化所有会用到的对象,即107个ShortLine和13个LongLine对象
4.在view处理点击事件的地方,计算出需要展示的对象位置,获得静态对象池中的数组下标,从此开始复制数组。

众所周知,内存泄漏有一种情况,是容器类持有了对象的引用,那么在此例中是否有这种可能性呢?
思考一下,我们做了什么,在数组中引用了静态内部类的实例,这会不会导致内存泄漏呢?
带着这个问题,我决定做一个小的实验
1.创建一个静态数组,一个静态ArrayList
2.创建一个基类Fruit,一个实现类Apple
3.在程序开始,new100个Fruit对象,分别填充到数组和ArrayList中
4.点击跳转到下一个activity中,并且在之前的activity的生命周期函数中
跳转后做两个对比
(1)new100个Apple对象替换之前的fruit对象,观察之前的Fruit对象是否被回收
(2)clear ArrayList,把数组一个一个赋值null,观察Fruit对象的引用情况,对比做清除操作是否带来变化。

下面开始具体实验

第一种情况:

数组ArrayList分别加入100个new Fruit,然后跳转到下一个activity中

ArrayList

    private static final ArrayList<Fruit> fruitsList = new ArrayList<>();

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = findViewById(R.id.tv_to_next);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, MainActivity2.class));
            }
        });

        arrayListReference();
    }

    public void arrayListReference(){
        //test1.1
        for(int i = 0; i<100; i++){
            fruitsList.add(new Fruit(i));
        }
    }

跳转activity之前的dump信息,显示出fruit对象有100个,并且被ArrayList直接引用

1.1before_jump_mem_dump.png
跳转activity之后的dump信息,显示出fruit对象有100个,并且被ArrayList直接引用,此时可以说发生了内存泄漏
1.1after_jump_mem_dump.png

数组

    private static final Fruit[] fruitsArray = new Fruit[100];

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = findViewById(R.id.tv_to_next);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, MainActivity2.class));
            }
        });

        arrayReference();
    }

    public void arrayReference(){
        //test1.2
        for(int i = 0; i<100; i++){
            fruitsArray[i] = new Fruit(i);
        }
    }

跳转activity之前的dump信息,显示出fruit对象有100个,并且被直接引用


1.2before_jump_mem_dump_a.png

跳转activity之后的dump信息,显示出fruit对象有100个,并且被直接引用,此时可以说发生了内存泄漏

1.2after_jump_mem_dump_a.png

这种情况是可以抢救一下的,我们在activity的生命周期函数onPause中,可以释放一些引用。那么在这种情况下,又会发生什么呢?

ArrayList
(PS:不要被dump里的Fruit[]这个对象迷惑了,我为了省事,没有注销掉private static final Fruit[] fruitsArray = new Fruit[100];这行代码,忽略即可)
加入下列代码

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause: ");
        //test2.1
        fruitsList.clear();
    }

直接看跳转之后,dump情况如何

2.1after_clear_jump_mem_dump.png
有小伙伴可能会有疑问,woc,你这100个对象还在这里躺着呢啊!!!
不用着急,我们手动触发一下GC试一试
2.1after_clear_jump_mem_dump_GC.png
此时就会发现,这100个对象已经被回收了。内存泄漏问题可以说被解决了。

数组
加入下列代码

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause: ");
        //test2.2
        for (int i = 0; i < fruitsArray.length - 1;i++){
            fruitsArray[i] = null;
        }
    }

直接看跳转之后,dump情况如何,这次就不放手动GC的图了,有兴趣的小伙伴可以自行实践一下


2.2after_clear_jump_mem_dump_a.png

事情到这里,有聪明的小伙伴就要问了,如果替换元素会发生什么情况呢?项目中的实际情况,中也有替换的场景,那么被替换下来的元素会不会造成内存泄漏呢?
ArrayList
更新下列代码

    public void arrayListReference(){
        //test1.1
        for(int i = 0; i<100; i++){
            fruitsList.add(new Fruit(i));
        }
        fruitsList.clear();
        for(int i = 0; i<100; i++){
            fruitsList.add(new Apple(i));
        }
    }

这里已经搜不到Fruit实例了


1.1after_replace_mem_dump.png

但是搜索apple实例,还是存在的(PS:这里用数组替换的结果做示例,结果都是一样的。)


1.2after_replace_mem_dump_a.png

数组
更新下列代码
public void arrayReference(){
//test1.2
for(int i = 0; i<100; i++){
fruitsArray[i] = new Fruit(i * 2);
}

    for(int i = 0; i<100; i++){
        fruitsArray[i] = new Apple(i * 3);
    }
}

实验结束,总结一下,在activity生命周期中,如果容器类持有了一些对象的引用,需要在适当的时机(不再需要这些数据时)清除这些引用,不然有可能造成内存泄漏。如果有错误,还请大佬批评指正。
到此为止,其实还有一个拓展思考问题,为什么之前需要手动GC,后面替换之后直接就被回收了?

相关文章

  • 为何每次用完ThreadLocal都要调用remove()?

    什么是内存泄漏 Key 的泄漏 Value 的泄漏 如何避免内存泄露 什么是内存泄漏 内存泄漏指的是,当某一个对象...

  • Android 内存泄漏

    内存泄漏的原因 常见的内存泄漏与解决方法 检测内存泄漏 认识内存泄漏 根本原因就是当一个对象理应被回收的时候,因为...

  • 【Android测试】内存泄漏检测 LeakCanary

    什么是内存泄漏和内存溢出?内存泄漏有什么危害?LeakCanary检测内存泄漏? 内存泄漏(Memory Leak...

  • Android如何打造高质量的应用?( 三)

    内存泄漏内存泄漏简单来说就是没有回收不再使用的内存,排查和解决内存泄漏也是内存优化无法避开的工作之一。很多内存泄漏...

  • Android内存泄漏场景及解决方法

    本文包括以下内容: 内存泄漏原理 Android内存泄漏发生的情况 检测内存泄漏的工具、方法 如何避免内存泄漏 更...

  • 内存泄漏

    什么是内存泄漏引起内存泄漏的原因野指针,空指针,僵尸对象 1.什么是内存泄漏 内存泄漏(Memory Leak)是...

  • Android如何避免WebView内存泄漏

    Android如何避免WebView内存泄漏 什么是内存泄漏 内存泄漏通俗的讲就是你创建了一个对象,却没有在合适的...

  • 内存泄漏理解

    1.简单理解 内存泄漏指的是堆内存泄漏,栈不会发生内存泄漏,只会发生栈溢出(StackOverFlow)。简单一个...

  • JVM-001-环境搭建及一个小例子

    安装JDK 请查看这篇文章:认识JDK并配置path环境变量 一个内存泄漏分析的小例子 编写内存泄漏的代码 运行上...

  • Android内存管理及内存泄漏分析(二)

    4、常见内存泄漏 这是一个老生常谈的一个问题了,但我还是先对Java中的内存泄漏做一个定义: Java中的内存泄漏...

网友评论

      本文标题:内存泄漏的一个小思考

      本文链接:https://www.haomeiwen.com/subject/kujtjrtx.html