C#/XNA-将对象加载到内存-它是如何工作的?

C#/XNA-将对象加载到内存-它是如何工作的?,c#,memory,xna,C#,Memory,Xna,我从C#和XNA开始。在“游戏”类的“更新”方法中,我有以下代码: t = Texture2D.FromFile( [...] ); //t is a 'Texture2D t;' 它加载小图像。“Update”方法像循环一样工作,因此调用此代码 一秒钟内多次。现在,当我运行我的游戏时,它需要95MB的内存,慢慢地变为130MB左右(由于我发布的代码,没有这个代码,它仍然是95MB),然后立即变为100MB左右(garbare colletion?),然后再慢慢变为130MB,然后立即变为10

我从C#和XNA开始。在“游戏”类的“更新”方法中,我有以下代码:

t = Texture2D.FromFile( [...] ); //t is a 'Texture2D t;'
它加载小图像。“Update”方法像循环一样工作,因此调用此代码 一秒钟内多次。现在,当我运行我的游戏时,它需要95MB的内存,慢慢地变为130MB左右(由于我发布的代码,没有这个代码,它仍然是95MB),然后立即变为100MB左右(garbare colletion?),然后再慢慢变为130MB,然后立即变为100MB,依此类推。因此,我的第一个问题是:

  • 你能解释一下为什么它是这样工作的吗
  • 我发现,如果我将代码更改为:

    t.Dispose()
    t = Texture2D.FromFile( [...] );
    
    它的工作原理是这样的:首先需要95MB,然后慢慢变为101MB(由于 代码),并保持在此级别

  • 我不明白为什么需要6MB(101-95)

  • 我想让它像这样工作:加载映像、从内存释放、加载映像、从内存释放等等,所以程序应该总是需要95MB(在前面的方法中,映像只加载一次时需要95MB)。我应该使用什么说明

  • 如果很重要,图像的大小约为10KB


    谢谢大家!

    不应使用更新方法加载纹理,因为它加载了很多。在.net中,对象是垃圾收集的,这意味着您不能显式释放对象(即使.Dispose也不能这样做)

    如果系统压力很大,GC可能无法尽可能频繁地运行

    简而言之:您正在“泄漏”数千个纹理2D

    加载它们的正确方法是在一个初始化事件中,并将它们存储在类变量、字典、列表或任何结构中。基本上,只加载一次,然后重新使用

    如果必须按需加载纹理,只需加载一次,并将其存储在List/Dictionary/Class变量中,然后再次重用

    编辑:你的“加载映像,发布,加载,发布”的方法在.net中不起作用,因为你不能显式地释放内存。你可以调用GC.Collect,但是a)也不能保证它,b)GC.Collect速度非常慢


    是否有特定的原因使您必须每秒重新加载图像60次?如果您只需要每隔一秒左右重新加载一次,则可以使用elapsedGameTime来测量自上次.Update之后过期的时间,如果超过阈值,则重新加载图像。(您需要一个类变量,如“LastUpdated”,与之进行比较,并在重新加载图像时进行更新)

    首先,您需要了解您所做的事情非常奇怪

    加载纹理的“常规”方法是使用内容管道和内容管理器

    如果您真的必须从文件而不是内容系统加载纹理,那么您应该只为每个纹理加载一次。您应该在
    Game.LoadContent
    中使用
    Texture2D.FromFile
    ,并在
    Game.UnloadContent
    中调用
    Dispose
    (通常内容管理器会为您处理调用Dispose的操作-但由于您不需要通过内容管理器,因此您必须自己调用Dispose)


    需要意识到的重要一点是,您在这里使用的是托管资源和非托管资源

    托管对象使用的内存将由垃圾收集器处理——在本例中,Texture2D的每个实例都使用一小段托管内存。99%的时候你不需要担心它-垃圾收集器真的很擅长处理托管内存-这就是它的工作

    您确实需要担心的是非托管资源—在本例中,您正在加载的所有纹理所使用的纹理内存。最终,垃圾收集器将运行,查看所有这些未引用的Texture2D对象并收集它们。当它收集它们时,它将完成它们,这与调用Dispose一样,将释放非托管资源

    但是垃圾收集器无法“查看”这些Texture2D对象正在使用的所有非托管资源。它不知道什么时候需要释放这些对象——你自己可以做得更好。当不再需要纹理时,只需调用
    Dispose

    这是因为垃圾收集器不知道纹理所使用的30MB额外内存,当您不调用Dispose时,它正在累积。除此之外,当你查看进程内存使用情况时,你看不到的是那些纹理使用的GPU上的所有内存

    (Texture2D的底层实现是,它持有对DirectX纹理对象的引用,该对象本身是一个使用非托管主内存的非托管对象,该对象反过来处理纹理及其在GPU上的关联内存。Texture2D对象的托管部分仅约为100-200字节,用于存储上述引用ce和纹理宽度、高度、格式等的缓存。)


    现在-至于你想实现什么。如果你真的需要在每一帧加载一个纹理(这是非常不寻常的),那么你也不再需要以前的纹理

    首先,对每个未使用的纹理帧调用
    Dispose
    是一种有效的方法。您将依靠垃圾收集器清理由此创建的所有垃圾(Texture2D对象的托管端)-这在Windows上没有问题,但可能会降低您在Xbox上的性能。至少您不会泄漏非托管资源

    一个更好的方法,特别是当纹理每次大小相同时,就是继续使用相同的纹理对象。然后在每个帧上使用新的纹理数据调用
    Texture2D.SetData

    (如果您的纹理大小不同,或者在给定时间使用多个纹理,则可能需要实现纹理池之类的功能。)

    当然,LoadFile接受一个实际的文件,SetData接受原始数据