14

私はしばらくの間、XNA で「ボクセル」と呼ばれる小さなゲームを開発してきました。Minecraft のような .NET C# ゲーム。シンプルなコンセプトを使用して、ゲームからデータを保存および読み取り、世界地図を作成します。すべてがxmlファイルに保存されます。

今、私はより大きなマップを読み込もうとしていますが、マップの生成中に 'paf' 例外が発生しました:

[システムのメモリ不足例外]

例外が発生した後、ファイルはそれほど重くなく、約70 mbであるため、理由がわかりません。最大 70 MB の XML ファイルの生成中に例外が発生するのは正常ですか?

使用した開発環境

  • Microsoft Windows 7 (x64) ビット
  • Visual Studio 2012 プロフェッショナル Update 3
  • 8192ラム
  • Intel 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 関数で例外をキャッチします。詳細については、以下を参照してください

ここに画像の説明を入力

私の generatedNode 関数は再帰的で、「システムのメモリ不足例外」で例外が発生します

#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

0

まず、使用後は必ず FileStream を閉じてください。例) - これはベスト プラクティスの 1 つです。

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 に答える