找回密码
 立即注册
查看: 350|回复: 5

unityC#层到处new 数据结构或gameobject对象用完就不管了 ...

[复制链接]
发表于 2022-1-21 13:59 | 显示全部楼层 |阅读模式
写个极限的用例测试一下就知道了。(如果你在函数体内,而非外面持有引用是能干掉,写个for循环看看)。new object而非gameobject哦,因为gameobject会被全局持有引用。所以new gameobject可不是能用完不管。要自己destroy的呢。
但是项目中真不管,最容易出问题的地方在于没有正确清理引用导致明明应该被消除的引用没有被正确消除。常见的是设置了委托然后没有在正确的时机及时移除。这种实际上把引用间接挂到全局数据。是不符合gc条件的。有时候哪怕gameobject在切换了场景被干掉依然不能被干掉。
你写个用例把一个gameobject干掉,用==判空是true,但是用object. ReferenceEqual判空却是false。只要还持有wrap层的引用,即c#对象的引用内存是没法彻底回收的。
然后另外一个更深的问题取决于虚拟机底层垃圾回收的实现。有的时候会把堆撑大而不会降回去。还有取决于c++底层的一些容器的算法的实现,比如vector扩容后哪怕你clear了所有元素都不会把内存真正释放。那么底层的好多数据结构的容器都有越运行占用内存越多的情况。那么换stl的list是否可以解决呢?
遗憾的是同样不能,因为allocator同样是先占一片的策略。
还有lua虚拟机的内存分配,同样的规则。
所以,当你面临实际开发的过程,要考虑理论边际和实际问题。理论边际按标记垃圾回收的原理一定可以把垃圾对象找出来。我用c++纯手撸过一次,本质上是有向图的全遍历。先把所有对象标记为不可达,然后从根集开始递归遍历,遍历过程标记可达。最后结束剩下的没被标记的就是垃圾内存。从原理上你实现了反射原表就能拿到子引用。这里就不对分代回收期原理展开了。
但是,难的地方是可达性规则只要持有强引用就是能通过递归访问的标记可达的。这个是你用脚本开发项目的时候一定要意识到的问题。比如c#和lua相互持有引用。这种引用关系大部分时候存在于全局状态的绑定。即根集,你如果处理得不好,很容易出现因为引用错误而泄露内存。
因此,在unity开发过程,问题不是一直new能不能被释放。而是错误的引用如何在正确的时机消除。
发表于 2022-1-21 14:01 | 显示全部楼层
写个极限的用例测试一下就知道了。(如果你在函数体内,而非外面持有引用是能干掉,写个for循环看看)。new object而非gameobject哦,因为gameobject会被全局持有引用。所以new gameobject可不是能用完不管。要自己destroy的呢。
但是项目中真不管,最容易出问题的地方在于没有正确清理引用导致明明应该被消除的引用没有被正确消除。常见的是设置了委托然后没有在正确的时机及时移除。这种实际上把引用间接挂到全局数据。是不符合gc条件的。有时候哪怕gameobject在切换了场景被干掉依然不能被干掉。
你写个用例把一个gameobject干掉,用==判空是true,但是用object. ReferenceEqual判空却是false。只要还持有wrap层的引用,即c#对象的引用内存是没法彻底回收的。
然后另外一个更深的问题取决于虚拟机底层垃圾回收的实现。有的时候会把堆撑大而不会降回去。还有取决于c++底层的一些容器的算法的实现,比如vector扩容后哪怕你clear了所有元素都不会把内存真正释放。那么底层的好多数据结构的容器都有越运行占用内存越多的情况。那么换stl的list是否可以解决呢?
遗憾的是同样不能,因为allocator同样是先占一片的策略。
还有lua虚拟机的内存分配,同样的规则。
所以,当你面临实际开发的过程,要考虑理论边际和实际问题。理论边际按标记垃圾回收的原理一定可以把垃圾对象找出来。我用c++纯手撸过一次,本质上是有向图的全遍历。先把所有对象标记为不可达,然后从根集开始递归遍历,遍历过程标记可达。最后结束剩下的没被标记的就是垃圾内存。从原理上你实现了反射原表就能拿到子引用。这里就不对分代回收期原理展开了。
但是,难的地方是可达性规则只要持有强引用就是能通过递归访问的标记可达的。这个是你用脚本开发项目的时候一定要意识到的问题。比如c#和lua相互持有引用。这种引用关系大部分时候存在于全局状态的绑定。即根集,你如果处理得不好,很容易出现因为引用错误而泄露内存。
因此,在unity开发过程,问题不是一直new能不能被释放。而是错误的引用如何在正确的时机消除。
发表于 2022-1-21 14:11 | 显示全部楼层
请搜索 GC 相关内容,对象除了构造函数之外还有个析构函数,可以了解一下一个对象从构造到释放的执行过程。
而且也不是所有对象都可以不管,具体看是托管资源还是非托管资源,可以搜索 IDisposable 接口,了解 using 的用法。
发表于 2022-1-21 14:16 | 显示全部楼层
可以看看mono的源码,有可能当前版本真有一个bug会造成内存泄露并且被你发现了。
发表于 2022-1-21 14:21 | 显示全部楼层
内存泄露的本质是什么,一块内存分配了然后不返回给系统,将会导致系统可用内存越来越少,导致在未来莫一刻会无内存可用,C#分托管内存和非托管模式,托管内存会交给CRL去管理,如果到处都new从语言层面看没啥问题,但是造成大量GC效应,引起卡顿,如果非托管内存,就会退化成C语言的感觉,是需要自己管理内存的
发表于 2022-1-21 14:24 | 显示全部楼层
你应该看一下c#,这些东西其实跟unity没啥关系。
到处new不会泄露,都是托管的。没用了就gc掉了。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 21:17 , Processed in 0.065760 second(s), 22 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表