14

我在 XNA 中开发了一个名为“voxel”的小游戏已经有一段时间了。一个类似 Minecraft 的 .NET C# 游戏。我使用一个简单的概念来保​​存和读取我的游戏中的数据来构建世界地图。一切都存储在一个 xml 文件中。

现在我正在尝试加载更大的地图,并且在生成地图期间引发了“paf”异常:

[系统内存不足异常]

我不明白为什么,因为引发异常后我的文件不是很重,大约70 mb在生成高达 70 mb的 XML 文件期间出现异常是否正常?

使用的开发环境

  • Microsoft Windows 7 (x64) 位
  • Visual Studio 2012 专业更新 3
  • 8192 公羊
  • 英特尔 i5 CPU 2.5 Ghz (4 cpu)

我确实使用 CLR 分析器进行了测试,以检查垃圾收集器的工作原理:

在此处输入图像描述 在此处输入图像描述

我初始化 XmlWritter 的代码:

    private XmlTextWriter myXmlTextWriter ;


    #region prepareNewWorldXmlFIle
    private void PreparedNewWorldXmlFile()
    {
        Stream fs = new FileStream(currentPath + "World\\world.xml", FileMode.Create);

        myXmlTextWriter = new XmlTextWriter(fs,Encoding.ASCII);
        myXmlTextWriter.Formatting = Formatting.Indented;
        myXmlTextWriter.WriteStartDocument(false);
        myXmlTextWriter.WriteComment("World Map ID:");
        //World and his attribute
        myXmlTextWriter.WriteStartElement("World");
        myXmlTextWriter.WriteStartElement("Matrix", null);
        myXmlTextWriter.WriteStartElement("Regions");
    }
    #endregion

我用来生成世界地图并编写 xml 文件的代码:

    //Octree calcul and generate map to xml file
    foreach (Region region in arcadia.world.Regions)
    {
        isAllCheckSameIdOctree = false;
        if (isFirstGenerationWorld)
        {
            //Regions and attributes
            myXmlTextWriter.WriteStartElement("Region");
            myXmlTextWriter.WriteAttributeString("id", indexRegion.ToString());
            myXmlTextWriter.WriteAttributeString("min", "x:" + region.PositionMin.X + ";y:" + region.PositionMin.Y + ";z:" + region.PositionMin.Z);
            myXmlTextWriter.WriteAttributeString("max", "x:" + region.PositionMax.X + ";y:" + region.PositionMax.Y + ";z:" + region.PositionMax.Z);
            myXmlTextWriter.WriteStartElement("Structures");
            myXmlTextWriter.WriteAttributeString("type", "cube");
        }
        indexRegion++;
        if (region.Matrice != null)
        {
            //If the node to generate contain minimum a height divisible by 2
            if (((region.PositionMax.Y - region.PositionMin.Y) / 2) > 2)
            {
                //generate and octree by 8
                GenerateNodes(region, region.PositionMin, 8);
            }
            else if (((region.PositionMax.Y - region.PositionMin.Y) / 2) <= 2)
            {
                //generate and octree by 4
                GenerateNodes(region, region.PositionMin, 4);
            }
            while (!isAllCheckSameIdOctree)
            {
                if (nodeToRegenerate != null && needRecurseBuild)
                {
                    //if the node is greater than 2
                    if (nodeToRegenerate.TotalHeight > 2)
                    {
                        nodeToRegenerate = GenerateNodes(nodeToRegenerate, region, nodeToRegenerate.Position, 8);
                        if (nodeToRegenerate == null)
                        {
                            isAllCheckSameIdOctree = true;
                        }

                    }
                    else if (nodeToRegenerate.TotalHeight <= 2)
                    {
                        nodeToRegenerate = GenerateNodes(nodeToRegenerate, region, nodeToRegenerate.Position, 4);
                        if (nodeToRegenerate == null)
                        {
                            isAllCheckSameIdOctree = true;
                        }
                    }
                }
                else
                {
                    isAllCheckSameIdOctree = true;
                }
            }
            if (isFirstGenerationWorld)
            {
                myXmlTextWriter.WriteEndElement();//Ferme le noeud Structures
                myXmlTextWriter.WriteEndElement();//Ferme le noeud Region
                myXmlTextWriter.Flush();
            }
        }
        else
        {
            if (isFirstGenerationWorld)
            {
                myXmlTextWriter.WriteEndElement();//Ferme le noeud Structures
                myXmlTextWriter.WriteEndElement();//Ferme le noeud Region
                myXmlTextWriter.Flush();
            }
        }
    }
    if (isFirstGenerationWorld)
    {
        myXmlTextWriter.WriteEndElement();//Ferme le noeud Regions
        myXmlTextWriter.WriteEndElement();//Ferme le noeud World
        myXmlTextWriter.Flush();
        myXmlTextWriter.Close();
    }

在我的 generatedNode 函数中捕获异常,有关更多信息,请参见下文

在此处输入图像描述

我的 generateNode 函数递归,其中异常触发“系统内存不足异常”

#region ReGenerateWorld
    private Node GenerateNodes(Node nodeToRegenerate, Region region, Vector3 position, int countToCut)
    {
        //Relative dimension of the parent octree
        int widthParent = (int)nodeToRegenerate.TotalWidth / 2;
        int heightParent = (int)nodeToRegenerate.TotalHeight / 2;
        int lenghtParent = (int)nodeToRegenerate.TotalLenght / 2;

        //Relative dimension of the parent octree
        int widthNode = (widthParent) / (countToCut / (countToCut / 2));
        int heightNode = (heightParent) / (countToCut / (countToCut / 2));
        int lenghtNode = (lenghtParent) / (countToCut / (countToCut / 2));

        if (heightNode < 1)
        {
            heightNode = 1;
        }

        int refX = (int)position.X / 2;
        int refY = (int)position.Y / 2;
        int refZ = (int)position.Z / 2;
        int indexStartX = 0;
        int indexStartY = 0;
        int indexStartZ = 0;
        int nbrToCut = 0;
        if (heightParent >= 2)
        {
            nbrToCut = ((widthParent / (widthParent / 2))) * ((heightParent / (heightParent / 2))) * ((lenghtParent / (lenghtParent / 2)));
        }
        else
        {
            nbrToCut = 4;
            heightNode = 1;
        }
        //Calculate the number of cubic to cut

        //Génére les noeud racine
        int countVertical = 0;
        int calcPosX = 0;
        int calcPosY = 0;
        int calcPosZ = 0;
        int[][][] nodeMatriceWorld = null;
        bool firstTime;
        newNode = null;
        int idGroup = 0;
        bool isSameId = true;
        int idToCheck = 0;

        for (int index = 0; (index < nbrToCut) && (refY < 32); index++)
        {
            indexStartX = refX;
            indexStartY = refY;
            indexStartZ = refZ;

            try
            {
                nodeMatriceWorld = new int[widthNode][][];
                for (int i = 0; i < widthNode; i++)
                {
                    nodeMatriceWorld[i] = new int[lenghtNode][];
                    for (int j = 0; j < lenghtNode; j++)
                    {
                        nodeMatriceWorld[i][j] = new int[heightNode];
                    }
                }
            }
            catch (Exception ex)
            {
                // OUT OF MEMORY EXCEPTION HERE
                Console.Out.WriteLine(ex.Message);
            }
            firstTime = true;
            for (int epaisseur = 0; epaisseur < heightNode; epaisseur++, indexStartY++)
            {
                for (int ligne = 0; ligne < lenghtNode; ligne++, indexStartZ++)
                {
                    for (int collone = 0; collone < widthNode; collone++, indexStartX++)
                    {
                        if (firstTime)
                        {
                            calcPosX = indexStartX;
                            calcPosY = indexStartY;
                            calcPosZ = indexStartZ;

                            firstTime = false;
                        }
                        nodeMatriceWorld[collone][ligne][epaisseur] = matriceWorld[indexStartX][indexStartZ][indexStartY];
                    }

                    indexStartX = refX;
                }

                indexStartZ = refZ;
            }

            indexStartY = refY;
            idGroup = matriceWorld[calcPosX][calcPosZ][calcPosY];
            countVertical++;
            if (newNode != null)
            {
                newNode.Dispose();
            }

            newNode = new Node(nodeMatriceWorld, new Vector3(calcPosX, calcPosY, calcPosZ), idGroup, widthNode, heightNode, lenghtNode);
            region.Nodes[idGroup].Add(newNode);

            //Regions.Add(new Node(nodeMatriceWorld, new Vector3(calcPosX, calcPosY, calcPosZ), idGroup));
            refX += widthNode;

            if (countVertical >= 4)
            {
                refY = ((int)position.Y / 2) + heightNode;
                refX = ((int)position.X / 2);
                refZ = (int)position.Z / 2;
                countVertical = 0;
            }
            else if (countVertical == 2)
            {
                refZ = ((int)position.Z / 2) + lenghtNode;
                refX = ((int)position.X / 2);

            }
        }

        isSameId = true;
        nodeToRegenerate = null;
        needRecurseBuild = false;
        idToCheck = 0;
        // Check for each octree node if all are the same id
        foreach (List<Node> listNode in region.Nodes)
        {
            foreach (Node node in listNode.Where(m => m.isGroupSameId == false))
            {

                isSameId = true;
                idToCheck = node.matriceNode[0][0][0];
                node.isGroupSameId = true;//Le met a true au depart
                for (int epaisseur = 0; epaisseur < node.TotalHeight / 2 && isSameId; epaisseur++)
                {
                    for (int ligne = 0; ligne < node.TotalLenght / 2 && isSameId; ligne++)
                    {
                        for (int collone = 0; collone < node.TotalWidth / 2 && isSameId; collone++)
                        {
                            if (node.matriceNode[collone][ligne][epaisseur] != idToCheck)
                            {
                                isSameId = false;//si au moin un cube est différent on le marque
                                node.isGroupSameId = false;
                                //node.ItemGroup = node.matriceNode[collone, epaisseur, ligne];
                                nodeToRegenerate = node;
                                needRecurseBuild = true;
                                break;
                            }

                        }
                    }

                }

                if (!isSameId)
                {

                    break;
                }
                else
                {
                    if (idToCheck != 0)
                    {
                        isSameId = true;
                        node.isGroupSameId = true;
                        node.ItemGroup = idToCheck;
                        node.matriceNode = null;
                        node.Cube = new Primitives3D.Cube(node.ItemGroup, node.Position, new Vector3(0, 0, 0), node.TotalWidth, node.TotalHeight, node.TotalLenght);
                        //Initialise le cube qui représente le noeud
                        node.Cube.BuildCubeStart();
                        if (isFirstGenerationWorld)
                        {
                            myXmlTextWriter.WriteStartElement("Cube");
                            //Structures et ses attributs
                            myXmlTextWriter.WriteAttributeString("id", node.ItemGroup.ToString());
                            myXmlTextWriter.WriteAttributeString("min", "x:" + node.Cube.BoundingBox.Min.X + ";y:" + node.Cube.BoundingBox.Min.Y + ";z:" + node.Cube.BoundingBox.Min.Z);
                            myXmlTextWriter.WriteAttributeString("max", "x:" + node.Cube.BoundingBox.Max.X + ";y:" + node.Cube.BoundingBox.Max.Y + ";z:" + node.Cube.BoundingBox.Max.Z);

                            myXmlTextWriter.WriteEndElement();//Ferme le noeud xml cube
                            myXmlTextWriter.Flush();

                        }
                        //Ajoute l'id du noeud aux groupe d'id de la region s'il n'y était pas auparavant
                        if (!region.IdFound.Contains(node.ItemGroup) && node.ItemGroup != 0)
                        {
                            region.IdFound.Add(node.ItemGroup);
                        }
                    }
                    // If the node group is equal to an empty group id -> 0 it removes entire                        else
                    {
                        nodeToDelete = node;
                    }
                }
            }
            if (!isSameId)
            {

                break;
            }
            //Console.Out.WriteLine("Nodes cout generated : " + Nodes.Count.ToString());

        }
        // If a node does not contain all the same id -> go remove it
        if (!isSameId)
        {
            region.Nodes[nodeToRegenerate.ItemGroup].Remove(nodeToRegenerate);

        }
        if (nodeToDelete != null)
        {
            region.Nodes[nodeToDelete.ItemGroup].Remove(nodeToDelete);
        }
        nodeToDelete = null;
        //Dispose the resources
        newNode.Dispose();
        nodeMatriceWorld = null;


        return nodeToRegenerate;
    }
    #endregion

例外:

在此处输入图像描述

4

6 回答 6

1

据我了解,您的问题不在于 XML,而在于锯齿数组int[][][] nodeMatriceWorld。它的大小太大,或者您创建此数组的次数太多。这是您的代码:

try
{
    nodeMatriceWorld = new int[widthNode][][];
    for (int i = 0; i < widthNode; i++)
    {
        nodeMatriceWorld[i] = new int[lenghtNode][];
        for (int j = 0; j < lenghtNode; j++)
        {
            nodeMatriceWorld[i][j] = new int[heightNode];
        }
    }
}
catch (Exception ex)
{
    // OUT OF MEMORY EXCEPTION HERE
    Console.Out.WriteLine(ex.Message);
}

检查宽度节点、长度节点和高度节点。对于 nodeMatriceWorld,您至少需要 sizeof(int) * widthNode * lenghtNode * heightNode 字节的内存。

于 2016-07-18T16:52:21.403 回答
0

当您用完堆栈内存时,您可能会使用递归方法出现内存不足异常。基本上,您的递归是无限的或接近它足以耗尽为堆栈保留的内存。

于 2014-10-23T15:13:18.047 回答
0

我最近通过 XSLT 处理 XML 时遇到了类似的错误。然而,我的文件只有 5MB。我将 XSLT 处理更改为 .net 类(使用 MS 转换工具进行转换)并解决了问题。显然 XSLT 处理器存在一些错误。

您可能遇到类似的问题,并且根据其他用户的建议尝试使用不同的 XML 文档处理器,因为您使用的可能有错误或限制。

于 2014-11-20T11:57:24.260 回答
0

加载代码中的 widthNode、lengthNode 和 heightNode 有多大?如果出现一些不合理的值,您最终可能会分配一个巨大的 int 数组 - 并且在大对象堆上分配了足够大的对象,这更难管理 - 首先,它不会被压缩。所以你会有一个 3 GB 的进程,它只使用 70 MB 的内存。

是否会在重复加载(和释放)时发生异常,还是在每次应用程序运行仅加载一个节点时也会发生异常?CLR 探查器中按地址查看的对象有助于查看实际内存安排 - 如果堆碎片是您的问题,您将很容易看到可用空间间隙。

最后但并非最不重要的一点是,出于某种原因,OutOfMemoryException 可能完全错误 - 通常的罪魁祸首往往是在数组构造函数中获取错误的值(例如 new int[int.MaxValue] 将失败)。

于 2013-11-27T09:31:16.153 回答
0

看来您尝试使用高度图表示具有 3 维数组的地图,但您只需要 2 维。

示例:数组:int map[x][y]; 其中 map[x][y] 的值是 z。

这样你需要更少的内存来解决你的问题

于 2017-02-15T11:29:50.497 回答
0

首先,请确保在使用后关闭 FileStream。示例) - 这是最佳实践之一。

private void PreparedNewWorldXmlFile()
{
    using(Stream fs = new FileStream(currentPath + "World\\world.xml", FileMode.Create))
    {
        myXmlTextWriter = new XmlTextWriter(fs,Encoding.ASCII);
        myXmlTextWriter.Formatting = Formatting.Indented;
        myXmlTextWriter.WriteStartDocument(false);
        myXmlTextWriter.WriteComment("World Map ID:");
        //World and his attribute
        myXmlTextWriter.WriteStartElement("World");
        myXmlTextWriter.WriteStartElement("Matrix", null);
        myXmlTextWriter.WriteStartElement("Regions");
    }
}

我认为您应该在您的区域类实例中释放所有节点对象。因为 GC 不会收集您的区域类实例,但是您将其设置为 null。

于 2016-12-08T00:55:31.950 回答