美文网首页
笔记:一个关于形参的内存回收的问题

笔记:一个关于形参的内存回收的问题

作者: zhangyu1991 | 来源:发表于2021-04-23 19:07 被阅读0次

今天踩了一个坑,被测试找到排查问题的过程一脸懵逼,由于问题稍显诡异而且搞出问题的做事方式比较符合我的思维习惯,必须记录下来,加深印象,严加警醒,防止以后记忆模糊了再犯同样的错误...
这里出问题的代码可以大概简略成下面的模型:

class Test {

    fun run() {
        test(1)
        test(2)
        test(3)
    }

    var a: A? = null;
    private fun test(i: Int) {
        Log.d("test", "param i = $i")//重点注意,i的值(1)
        if (null == a) {
            a = A();
            a?.setCallback(object : A.Callback {
                override fun onCallback() {
                    Log.d("test", "i = $i")//重点注意,i的值(2)
                }
            })
        }
        a?.doIt()
    }

    class A {
        fun doIt() {
            mCallBack?.onCallback()
        }

        lateinit var mCallBack: Callback
        fun setCallback(callback: Callback) {
            mCallBack = callback
        }

        interface Callback {
            fun onCallback()
        }
    }
}

按理来说,调用三次test(),传入三个不同的值,执行下去应该也是三个不同的结果才对。但是由于形参内存回收方面的问题,在这种逻辑下,打印出来的日志是这样的:

日志
第一处和第二处两处位置引用同一个形参,打出来的值是不一样的...
第一处的值都是实时传进来的值,但是第二处的值,全部都是第一次传入的那个值。
我都懵逼了....
在短暂的懵逼之后大概捋了捋,基本能判断出是判空那里出的问题,由于有判空的逻辑存在,第二三次调用时代码是没有执行到第二次日志那里面的位置的,由此引发了大问题。
其实这里这样写的本意是为了节约运行内存,避免每次都去创建一个新的A对象,只创建一次,之后就反复使用。但是在这里的这个逻辑框架下,反而挖了一个大坑,把自己给埋进去了~
从出现的问题和代码的结构基本可以断定,第一次传进来的形参一直没有被回收,而且再后续的调用中被反复使用了,于是为了验证这个猜想,我把两次日志位置形参的内存地址打印了出来。
形参内存地址
可以看到第二处位置形参的内存地址是一模一样的,只有第一处形参的内存地址发生了变动,这样基本就验证了猜想是对的。按常理,形参在调用方法执行完成之后内存应该会立刻被回收掉,但是这里没有,因为这里a是一个全局变量,且一直存在,而给a设置的匿名Callback对象也因为a的一直存在而没有被回收掉内存,这个Callback对象在回调时又使用了这个形参,这个形参占用的内存空间也一直没有被回收掉,再加上调用方法的时候因为判空的原因代码没有执行进去,所以每次调用方法执行的都是第一次传进去的那个值
天坑啊!
其实这个形参没有被回收的原因,涉及到编译方面的知识,我也并不是十分明确,只是大概确定了问题点,但是目前是可以基本明确导致问题的写法了。
这种问题再叠加一些其他偶然的因素,有时候会隐藏的很深,并且表现出来奇奇怪怪,排查起来也不会那么顺,挺坑人的...
这一把记忆很深刻....再也不能这样写了..

相关文章

  • 笔记:一个关于形参的内存回收的问题

    今天踩了一个坑,被测试找到排查问题的过程一脸懵逼,由于问题稍显诡异而且搞出问题的做事方式比较符合我的思维习惯,必须...

  • ES6 函数中声明报错问题 笔记

    关于ES6中函数形参默认值时,函数形参多次声明相同变量的形参报错的问题: 当函数形参中有 y = 4;相当于let...

  • 《深入理解JVM虚拟机》- JVM内存管理之垃圾回收

    关于垃圾回收,主要回答两个问题:哪些内存需要回收、如何回收。 一、哪些内存需要回收?- 对象存活判定算法 1. 堆...

  • inline、explicit关键字小结

    inline 发生函数调用时,首先在栈内存上为形参分配内存,再将实参的值复制给形参,在函数局部还需要为变量分配内存...

  • 垃圾收集器

    问题: 1、哪些内存需要回收 2、何时回收 3、如何回收 问题一: 判断堆内存哪些对象已经“死亡”,需要进行回收?...

  • 24

    ——函数的参数—— ——形参和实参—— 形参:只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元...

  • 深入理解Java虚拟机读书笔记 三

    垃圾回收需要解决的三个问题是: 哪些内存需要回收 何时回收 如何回收 哪些内存需要回收 对于Java内存运行时区域...

  • 牛客网Java练习4

    1.下列哪些语句关于内存回收的说明是正确的? ( ) A 程序员必须创建一个线程来释放内存B 内存回收程序负责释...

  • Android经典面试题

    1.下列哪些语句关于内存回收的说明是正确的? (b )A、 程序员必须创建一个线程来释放内存 B、内存回收程序负责...

  • JVM:垃圾收集器与内存分配策略(上)

    概述 要研究的问题 回收哪些内存 什么时候回收 如何回收 为什么要了解GC和内存分配:需要排查各种内存溢出、内存泄...

网友评论

      本文标题:笔记:一个关于形参的内存回收的问题

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