jim*_*zer 8 c# mono foreach garbage-collection unity-game-engine
我的朋友在C#上使用Unity3D.他特意告诉我:
每个"foreach"循环的每次迭代都会生成24字节的垃圾内存.
我也在这里看到这些信息
但这是真的吗?
与我的问题最相关的段落:
用简单的"for"循环替换"foreach"循环.出于某种原因,每个"foreach"循环的每次迭代都会生成24字节的垃圾内存.一个简单的循环迭代10次,剩下240字节的内存准备收集,这是不可接受的
Mar*_*ell 12
foreach是一个有趣的野兽; 很多人错误地认为它与IEnumerable[<T>]/有关IEnumerator[<T>],但事实并非如此.因此,毫无意义地说:
每个"foreach"循环的每次迭代都会生成24字节的垃圾内存.
特别是,它取决于你正在循环的内容.例如,很多类型都有自定义迭代器 - List<T>例如 - 有一个struct基于迭代器的迭代器.以下分配零对象:
// assume someList is a List<SomeType>
foreach(var item in someList) {
// do something with item
}
Run Code Online (Sandbox Code Playgroud)
但是,这个对象分配:
IList<SomeType> data = someList;
foreach(var item in data) {
// do something with item
}
Run Code Online (Sandbox Code Playgroud)
看起来相同,但不是 - 通过改变foreach迭代IList<SomeType>(没有自定义GetEnumerator()方法,但实现IEnumerable<SomeType>),我们强迫它使用IEnumerable[<T>]/ IEnumerator<T>API,这很大程度上涉及一个对象.
编辑警告:注意我在这里假设unity可以保留自定义迭代器的值类型语义; 如果团结被迫将结构提升为物体,那么坦率地说所有的赌注都是关闭的.
碰巧的是,for如果每个分配都很重要,我会很高兴地说"确定,使用和在这种情况下使用索引器",但从根本上说:全面的声明foreach是无益的和误导性的.
Chr*_*ell 10
你的朋友是正确的,从版本4.3.4开始,以下代码将在Unity中每帧产生24k的垃圾:
using UnityEngine;
using System.Collections.Generic;
public class ForeachTest : MonoBehaviour
{
private string _testString = "this is a test";
private List<string> _testList;
private const int _numIterations = 10000;
void Start()
{
_testList = new List<string>();
for(int i = 0; i < _numIterations; ++i)
{
_testList.Add(_testString);
}
}
void Update()
{
ForeachIter();
}
private void ForeachIter()
{
string s;
for(int i = 0; i < 1000; ++i)
{
foreach(string str in _testList)
{
s = str;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这显示了Unity Profiler中的分配:

根据以下链接,它是Unity使用的单声道版本中的一个错误,它强制结构枚举器被装箱,这似乎是一个合理的解释,虽然我没有通过代码检查验证:博客文章讨论拳击错误
关于foreach,这里有一些很好的建议,但是我必须对Mark上面关于标签的评论感到不满(不幸的是,我的评论下面没有足够的代表评论),他说,
实际上,我必须用一小撮盐来阅读那篇文章,因为它声称"在一个对象上调用标签属性来分配和复制额外的内存" - 这里的标签是一个字符串 - 抱歉,但这根本不是真的 - 所有发生的是对现有字符串对象的引用被复制到堆栈上 - 这里没有额外的对象分配. -
实际上,调用tag属性确实会产生垃圾.我的猜测是内部标签存储为整数(或其他一些简单类型),并且当调用tag属性时,Unity使用内部表将int转换为字符串.
它违背了原因,因为tag属性可以很容易地从该内部表返回字符串而不是创建一个新字符串,但无论出于何种原因,这都是它的工作原理.
您可以使用以下简单组件自行测试:
public class TagGarbageTest : MonoBehaviour
{
public int iterations = 1000;
string s;
void Start()
{
for (int i = 0; i < iterations; i++)
s = gameObject.tag;
}
}
Run Code Online (Sandbox Code Playgroud)
如果gameObject.tag没有产生垃圾,那么增加/减少迭代次数对GC Allocation(在Unity Profiler中)没有影响.事实上,随着迭代次数的增加,我们会看到GC分配的线性增长.
还值得注意的是,name属性的行为方式完全相同(再次,为什么我不能这样做).
| 归档时间: |
|
| 查看次数: |
11099 次 |
| 最近记录: |