Reputation: 3979
I've been developing for some time a small game called 'voxel' in XNA. A .NET C# like Minecraft game. I use a simple concept to save and read data from my games to build the world map. Everything is stored in an xml file.
Now I'm trying to load a larger map, and 'paf' an exception is raised during the generation of my map:
[System out of memory exception]
I do not understand why, because after the exception is raised my file is not very heavy, it's about 70 mb. Is it normal to see an exception raise during the generation of an XML file up to 70 mb?
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
//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();
}
#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
Upvotes: 14
Views: 3303
Reputation: 26
it appears you try to represent a map with an 3 dimensional array, using a heightmap however you only need 2 dimensions.
example: the array: int map[x][y]; where the value of map[x][y] is the z.
this way you need much less memory which should fix your problem
Upvotes: 0
Reputation: 856
First of all, please make sure close FileStream after using it. example) - this is one of best practice.
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");
}
}
I think you should release all node object at your region class instance. becuase GC will not collect your region class instance however you set it null.
Upvotes: 0
Reputation: 2413
As I understand, you problem is not with XML, but in jagged array int[][][] nodeMatriceWorld. Its size is too large or you're creating this array too many times. Here is your code:
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);
}
Check widthNode, lenghtNode and heightNode. For nodeMatriceWorld you need at least sizeof(int) * widthNode * lenghtNode * heightNode bytes of memory.
Upvotes: 1
Reputation: 13
I recently got a similar error processing XML through XSLT. My files were only 5MB however. I changed my XSLT processing into a .net class (conversion using MS conversion tool) and that sorted out the problem. Apparently some bug with the XSLT processor.
You might be having a similar problem and as suggested by the other user try using different XML document processors as the one u using may have a bug or limitation.
Upvotes: 0
Reputation: 1057
You may get out of memory exception with recursive methods when you run out of stack memory. Basically, your recursion is either infinite or close to it enough to exhaust the memory reserved for stack.
Upvotes: 0
Reputation: 63732
How large is widthNode, lengthNode and heightNode in your loading code? If some unreasonable value gets there, you could end up allocating a huge int array - and large enough objects get allocated on the large object heap, which is much harder to manage - for one, it doesn't get compacted. So you'd have a 3 GB process which only really uses 70 MB of memory.
Does the exception occur on repeated loading (and freeing), or does it also occur if you load just one node per application run? Objects by address view in CLR profiler is helpful for having a look at real memory arrangement - you'll see free space gaps quite easily if heap fragmentation is your problem.
And last but not least, it could be possible that the OutOfMemoryException is completely wrong for some reason - the usual culprit tends to be getting a wrong value in array constructors (new int[int.MaxValue] is going to fail, for example).
Upvotes: 0