关于c#:创建和写入XML文件会引发异常->系统内存不足异常?

Create and write an XML file throws the exception -> system out of memory exception?

我已经在XNA上开发了一款名为" voxel"的小型游戏。类似于Minecraft游戏的.NET C#。
我使用一个简单的概念来保??存和读取游戏中的数据以构建世界地图。一切都存储在xml文件中。

现在我正尝试加载更大的地图,并且在生成地图时会出现'paf'异常:

[System out of memory exception]

我不明白为什么,因为在引发异常之后,我的
文件不是很重,大约70 mb。
在生成最大70 mb的XML文件的过程中看到异常引发是正常的吗?

使用的开发环境

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

我确实使用CLR事件探查器进行了测试,以检查垃圾收集器的工作方式:

enter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    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文件的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
    //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();
    }

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

enter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#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

例外:

enter


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}

检查widthNode,lenghtNode和heightNode。对于nodeMatriceWorld,您至少需要sizeof(int)* widthNode * lenghtNode * heightNode内存字节。


您似乎试图用3维数组表示地图,
使用高度图,但是您只需要2个维度。

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

这样,您需要的内存更少,应该可以解决问题


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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。


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

您可能遇到类似的问题,并且正如另一位用户建议的那样,请尝试使用不同的XML文档处理器,因为您使用的XML文档处理器可能存在错误或局限性。


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


您的加载代码中的widthNode,lengthNode和heightNode有多大?如果到达某个不合理的值,您最终可能会分配一个巨大的int数组-并且在大型对象堆上分配了足够大的对象,这很难管理-对于其中一个对象,它不会被压缩。因此,您将拥有一个3 GB的进程,该进程仅真正使用70 MB的内存。

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

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