引用, 强弱引用, 事件机制与垃圾回收的关系及应用法则

2024-04-03 11:38

本文主要是介绍引用, 强弱引用, 事件机制与垃圾回收的关系及应用法则,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

经常见朋友们提起 “资源占用” 与 “垃圾回收”机制, 此类情况有常常伴随 事件机制与显示列表相关问题,还有诸如此类的强弱引用的问题,关系错综复杂,令人困惑不小。在经过一番整理和测试研究后决定拿出来和大家一起分享一下心得,共同促进天地会的壮大。

先来说说一些基本概念( 不完全遵从于课本,理解不准确请见笑 ):

1.引用类型与值类型的关系:
   值类型属于简单数据类型( Nmber/String/uint...), 具有可复制性,就像一本书可以被发行数万册一样,每本书都出自于同一版,只是相同内容的复制而已;
  引用类型属于复杂数据类型( XML/Sprite/Object...), 具有唯一性,  如同一个人办了很多信用卡,但所有帐号都对应同一个人; 你可以被称为张三/经理/老公, 但这个世界只有一个你,You are the No.1
   引用可以被理解为 对象的一个 “标示”,是为了区分和操作对象起来方便一些, 如同每个人的名字一样。

2. 强引用与弱引用:
  强引用也就是我们通常所讲的引用,其存亡直接决定了所指对象的存亡。如果不存在指向一个对象的引用,并且此对象不再显示列表中,则此对象会被从内存中释放。
  弱引用除了不决定对象的存亡外,其他与强引用相同。即使一个对象被持有无数个若引用,只要没有强引用指向他,那麽其还是会被清除。没办法,还是 “强哥” 有面子。
  临时变量 < 弱引用 < 强引用 = 字段引用

3. AS3.0 是一个面向对象的事件驱动的高级语言,可见事件在其中的位置。 事件分为 "事件触发者", "事件对象",  "事件接收者 ". 事件可以大大降低各个对象之间的耦合性, 灵活的传递参数,在Caigrom Framework 事件的地位很重要。如果事件触发者没了,那么事件接收者也就不会接收到事件; 如果事件接收者没了, 那就等于事件不会被接收; 事件对象在事件执行完毕后会被释放。
鉴于篇幅这里就不过多的讲述了。

4. 垃圾回收
  垃圾回收是虚拟机中的内存管理机制,是为了清除掉内存中的闲置资源和实现内存碎片的整理。垃圾回收究竟何时运行我没有具体研究,有兴趣的朋友可以去自 行了解下, 不过经过测试发现 :当系统出现异常时( 例如 : IO_Error ) 垃圾回收会自动运行。其实现原则是这样的 : 对象不存在于显示列表,并且不存在指向对象的引用,那麽此对象会被从内存中释放,这句话听起来很简洁,但如果与其他情况混到一起 往往让人无所适从,不知道何种情况下对象才会被释放,下面将展开对此问题的逐步讨论:

     先看一段代码:
       private function loadTest() : void
       {
              var loader : URLLoader = new URLLoader();
               loader.addEventListener( Event.COMPLETE, loadedHandler );
               loader.loader( new URLRequest( "url" ) );

        }

        private function loadedHandler( evt : Event ) : void
        {
               ( evt.target  as Loader ).removeListener(  Event.COMPLETE, loadedHandler );
               // Do others.
         }
         
         函数 loadTest() 中声明了一个临时变量 "loader", 此事件触发者 "loader" 持有对 loadedHandler () 函数的引用,用来处理事件, 我们先来简单分析一下:
      A : loader 何时被回收?
         loader 是个临时变量,此引用是临时的,生存期很短暂,只在 loadTest() 函数内有效, 所以可以理解为 : 不存在指向 "loader" 的引用。但 loader 并不会马上被内存释放, 因为其还没有执行
      完, 当 加载完成或IO错误时 loader 才会被释放, 其持有的 loadedHandler() 函数的引用随之消亡。类似此类的还有 建立连接, 打开本地文件之类的情况, 只有执行完毕/关闭连接后 对象才
      可以被释放。

      B : 假设 此段代码 位于 TargetClass 中,  如果 TargetClass 也是被临时创建的, 那麽他何时被释放?
      只有当载入事件执行完毕时 TargetClass 才可以被释放,因为这时 loader 才可以被释放,loader 间接持有 TargetClass 的引用,loader被释放-> TargetClass 的引用 消亡 -> TargetClass 被释放。

      C : 如果  loader.addEventListener( Event.COMPLETE, loadedHandler, false, 0, true ) :  loader 持有 TargetClass 的弱引用, 这时谁先消亡,谁后消亡?
      弱引用不影响到对象的消亡, 所以 : TargetClass 会瞬间被释放, loader 执行完才会被释放, 也不会执行 loadedHandler();

         D:  当 loader 是 TargetClass 中的一个字段( TargetClass 的一个属性/变量) , 他们又将如何消亡?

         private var loader : URLLoader;
         private function loadTest() : void
         {
               loader = new URLLoader();
               loader.addEventListener( Event.COMPLETE, loadedHandler );
               loader.loader( new URLRequest( "url" ) );
        }

        TargetClass 间接 持有自身的引用, loader 执行期内, TargetClass 不能被回收, loader 执行完毕后 两者一起被回收。 象这种 : 对象间接(  不管间接多少层 ) 或直接持有自身的引用
     不会影响对象的释放,即使不清除事件监听和"实现间接的对象" 他们也会被释放。

     E : 如果向上述情况中,loader 采用弱引用监听, 那么谁先被释放?
     此时,TargetClass 会瞬间被释放, loader 执行完毕后被释放。那么有人问了: loader 是TargetClass中的一个字段, 怎么可以 TargetClass 先被释放, 而 loader 后被释放呢?
     其实 loader 只是 TargetClass 持有的一个引用字段, 他并不是 URLLoader 本身( 不能说TargetClass 包含URLLoader), TargetClass 释放后 loader 引用也就消除了, 但已被创建的
     URLLoader 等到执行完毕后才会被释放。

     重复上述原则: 对象不存在于显示列表, 且不存在指向对象的引用,那麽此对象会被从内存中释放. 那么对象如何才算被引用(直接/间接)?
     F : 如果我在 TargetClass 外部 建立一个对于 loader 的引用(  把loader 的作用域公开), 那麽是不是就等于我间接的持有了TargetClass 的引用, TargetClass 不会被释放呢?
       答案是 : TargetClass 会被释放, loader 即使执行完都不会被释放。 TargetClass 被释放时同时释放了 指向 URLLoader 的一个引用 "loader", TargetClass 外部还有一个引用指向 这个  URLLoader, 所以"这个URLLoader" 不会被释放。引用对象的的字段不能算是间接引用对象, 引用对象中的方法才算是间接引用对象( 可以看出对象更强调其行为 ),这样此对象被持有引用, 不会被释放。

    听起来似乎还是很乱, 没关系, 接触多了自然就会搞清楚他们之间的关系, 下面提出几点法则供大家参考:

    1. 尽可能让对象自身的存亡不要影响到别人, 所以尽可能的使用弱引用, 除非你有特殊情况。
    2.对于永远存在的对象, 永远让其持有其他对象的弱引用。 例如 stage.addEventListener( MouseEvent.MOUSE_MOVE, object.moveHandler, false, 0, true );
      3. 养成良好的习惯, 事件执行完一定要移除监听(  清除引用 ), 连接执行完一定要关闭连接.............., 自己产生的垃圾,自己清理,不要乱扔果皮和烟头......   
      4.如果一个对象不会被多次方访问,那么没必要给其分配一个字段引用, 如一些皮肤 , 只要将其添加到显示列表就OK了, 没必要给他个 引用,多一事不如少一事。
      5. 局部性的业务逻辑,尽可能不要用诸如 C airGorm 中的 全局事件, 冒泡法就可以解决, 全局性的逻辑再用全局事件, 局部耦合性可以高一点, 但全局一定要耦合小。

这篇关于引用, 强弱引用, 事件机制与垃圾回收的关系及应用法则的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/872812

相关文章

CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比

《CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比》CSS中的position属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布... css 中的 position 属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布局和层叠关

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

Maven 配置中的 <mirror>绕过 HTTP 阻断机制的方法

《Maven配置中的<mirror>绕过HTTP阻断机制的方法》:本文主要介绍Maven配置中的<mirror>绕过HTTP阻断机制的方法,本文给大家分享问题原因及解决方案,感兴趣的朋友一... 目录一、问题场景:升级 Maven 后构建失败二、解决方案:通过 <mirror> 配置覆盖默认行为1. 配置示

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir

Go语言中Recover机制的使用

《Go语言中Recover机制的使用》Go语言的recover机制通过defer函数捕获panic,实现异常恢复与程序稳定性,具有一定的参考价值,感兴趣的可以了解一下... 目录引言Recover 的基本概念基本代码示例简单的 Recover 示例嵌套函数中的 Recover项目场景中的应用Web 服务器中

Python使用Tkinter打造一个完整的桌面应用

《Python使用Tkinter打造一个完整的桌面应用》在Python生态中,Tkinter就像一把瑞士军刀,它没有花哨的特效,却能快速搭建出实用的图形界面,作为Python自带的标准库,无需安装即可... 目录一、界面搭建:像搭积木一样组合控件二、菜单系统:给应用装上“控制中枢”三、事件驱动:让界面“活”

如何确定哪些软件是Mac系统自带的? Mac系统内置应用查看技巧

《如何确定哪些软件是Mac系统自带的?Mac系统内置应用查看技巧》如何确定哪些软件是Mac系统自带的?mac系统中有很多自带的应用,想要看看哪些是系统自带,该怎么查看呢?下面我们就来看看Mac系统内... 在MAC电脑上,可以使用以下方法来确定哪些软件是系统自带的:1.应用程序文件夹打开应用程序文件夹

使用Python实现Windows系统垃圾清理

《使用Python实现Windows系统垃圾清理》Windows自带的磁盘清理工具功能有限,无法深度清理各类垃圾文件,所以本文为大家介绍了如何使用Python+PyQt5开发一个Windows系统垃圾... 目录一、开发背景与工具概述1.1 为什么需要专业清理工具1.2 工具设计理念二、工具核心功能解析2.

Jvm sandbox mock机制的实践过程

《Jvmsandboxmock机制的实践过程》:本文主要介绍Jvmsandboxmock机制的实践过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、背景二、定义一个损坏的钟1、 Springboot工程中创建一个Clock类2、 添加一个Controller