Hello, my question is about a c# on .net 2.0 application that I'm building. I am creating an encyclopedia-like area within the application that holds information about plants. I've created a few classes for this area that represent different sets of plant information. Whenever I try to view the details of a plant I'm getting the exception System.OutOfMemoryException and I am having trouble figuring out why. I am using many arrays all at once. Is it too much for .net to handle? Can anyone point me toward a more efficient way of coding this? Here's the portion of offending code:
Thanks,
Joe Dulany
{
ArrayList items = new ArrayList(); if (p.height.Length > 0){
PlantAttribute paHeight = new PlantAttribute();paHeight.AttributeName =
"Height:";paHeight.AttributeList = p.height.Split(
new char[] { ',' });items.Add(paHeight);
}
if (p.spread.Length > 0){
PlantAttribute paSpread = new PlantAttribute();paSpread.AttributeName =
"Spread:";paSpread.AttributeList = p.spread.Split(
new char[] { ',' });items.Add(paSpread);
}
if (p.growthRate.Length > 0){
PlantAttribute paGrowthRate = new PlantAttribute();paGrowthRate.AttributeName =
"Growth Rate:";paGrowthRate.AttributeList = p.growthRate.Split(
new char[] { ',' });items.Add(paGrowthRate);
}
if (p.bloomSeason.Length > 0){
PlantAttribute paBloomSeason = new PlantAttribute();paBloomSeason.AttributeName =
"Bloom Season:";paBloomSeason.AttributeList = p.bloomSeason.Split(
new char[] { ',' });items.Add(paBloomSeason);
}
if (p.bloomColor.Length > 0){
PlantAttribute paBloomColor = new PlantAttribute();paBloomColor.AttributeName =
"Bloom Color:";paBloomColor.AttributeList = p.bloomColor.Split(
new char[] { ',' });items.Add(paBloomColor);
}
if (p.hardiness.Length > 0){
PlantAttribute paHardiness = new PlantAttribute();paHardiness.AttributeName =
"Hardiness:";paHardiness.AttributeList = p.hardiness.Split(
new char[] { ',' });items.Add(paHardiness);
}
if (p.soilType.Length > 0){
PlantAttribute paSoilType = new PlantAttribute();paSoilType.AttributeName =
"Soil:";paSoilType.AttributeList = p.soilType.Split(
new char[] { ',' });items.Add(paSoilType);
}
if (p.exposure.Length > 0){
PlantAttribute paExposure = new PlantAttribute();paExposure.AttributeName =
"Light Needs:";paExposure.AttributeList = p.exposure.Split(
new char[] { ',' });items.Add(paExposure);
}
if (p.attracts.Length > 0){
PlantAttribute paAttracts = new PlantAttribute();paAttracts.AttributeName =
"Attracts:";paAttracts.AttributeList = p.attracts.Split(
new char[] { ',' });items.Add(paAttracts);
}
if (p.resists.Length > 0){
PlantAttribute paResists = new PlantAttribute();paResists.AttributeName =
"Resists:";paResists.AttributeList = p.resists.Split(
new char[] { ',' });items.Add(paResists);
}
if (p.attributes.Length > 0){
PlantAttribute paAttributes = new PlantAttribute();paAttributes.AttributeName =
"Other Attributes:";paAttributes.AttributeList = p.attributes.Split(
new char[] { ',' });items.Add(paAttributes);
}
StringBuilder writer = new StringBuilder(); int itemsCount = items.Count; if (itemsCount > 0){
pnl_Characteristics.Visible =
true;}
foreach (PlantAttribute pa in items){
for (int i = 0; i < 4; i++){
int x = 1; if (i == 0){ writer.Append(
"writer.Append(
"- "
{ writer.Append(
"writer.Append(
"{
writer.Append(
"i = 0;
}
x++;
}
}
Joe DulanyPosted Aug 13, 2007, 9:54 AM
Thanks so much! I followed your advice, Alan, and it's working perfectly now.
Here's my new loop structure:
int i = 0; foreach (PlantAttribute pa in items){
if (i == 0 || i == 8){ writer.Append(
"writer.Append(
"writer.Append(pa.AttributeName.Trim());
writer.Append(
""); foreach (String str in pa.AttributeList)- "
);
{
writer.Append(
"writer.Append(str.Trim());
writer.Append(
"");}
writer.Append(
"{
writer.Append(
"}
i++;
}
AlanPosted Aug 13, 2007, 5:31 AM
I think what's happened here is that you've got yourself into an infinite loop by resetting the for loop control variable 'i' to zero towards the bottom of the code you posted:
if (i == 3 || x == itemsCount)
{
writer.Append("");
i = 0; //** problem here
}
It's not normally a good idea to change the value of loop control variables from within the loop itself as it can lead to out of memory exceptions and other strange behaviour, so I'd rethink your logic here.
One other point I've noticed is that you're using StringBuilder to avoid string concatenation but then concatenating some strings before appending them! Take this code for example:
foreach (String str in pa.AttributeList)
{ writer.Append("
It would be more sensible here to use 3 Append()'s:
foreach (String str in pa.AttributeList)
{
writer.Append("
writer.Append(str.Trim()); // no need to use ToString() as str is already a string
writer.Append("
}
Joe DulanyPosted Aug 13, 2007, 2:12 AM
Thank you for your response, Mike.
The exception is being thrown at line 181, which says: writer.Append("");
A look at my Autos window reveals that the variables contain values consistent with only the first pass through the loop at the time when the exception is thrown.
Here is the stack trace for this error:
System.OutOfMemoryException was unhandled by user code
Message="Exception of type 'System.OutOfMemoryException' was thrown."
Source="mscorlib"
StackTrace:
at System.String.GetStringForStringBuilder(String value, Int32 startIndex, Int32 length, Int32 capacity)
at System.Text.StringBuilder.GetNewString(String currentString, Int32 requiredLength)
at System.Text.StringBuilder.Append(String value)
at PlantDetails.PrintPlantAttributes(plant p) in c:\inetpub\wwwroot\PlantDetails.aspx.cs:line 181
at PlantDetails.Page_Load(Object sender, EventArgs e) in c:\inetpub\wwwroot\PlantDetails.aspx.cs:line 74
at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)
at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)
at System.Web.UI.Control.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
Mike GoldPosted Aug 12, 2007, 10:12 PM
Hi,
It would probably help to determine exactly where the exception is being thrown (which line #). If you turn on CLR Exceptions in the Debugger (Debug->Exceptions in the menu), it will trap the error on the line number it occurs when running in debug mode. This way we can determine if it's an allocation causing the error.
Best,