BUG: MemoryStream throws System.OutOfMemoryException using v1.1

  • Thread starter Thread starter Joe Feser
  • Start date Start date
J

Joe Feser

This very simple code throws an error on an XP Box running sp1 and all the
latest hot fixes.

All the memory in the machine is allocated and you must exit the
application.

try
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
ms.Capacity = 16776704;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}


"System.OutOfMemoryException: Exception of type System.OutOfMemoryException
was thrown."

Any clue?

It can also be reproduced if you keep writing a 512 byte array into the
MemoryStream until ensureCapacity tries to set the internal byte[] length to
16776704



Joe Feser
 
And if you change the code to
32000000

it will not throw an exception.

this does not allow you to just let the MemoryStream just do it's thing.

Just pointing this out to show it's not the machine.

Over 800mb are allocated with the ms.Capacity = 16776704 call.

Joe
 
Joe,

it's not the problem of MemoryStream, which just occupies the given amount
of bytes in a byte-array. It's a problem of System.Array. Try to allocate a
byte (or whatever array is derived from System.Array) -array, it will
produce the same error. Try to allocate it with 32.000.000, it's running
fine.

Weird.

In my opinion it's a bug in the internal handler of System.Array (With
Rotor, I tried to track it down to InternalCreate which resolves in
COMArrayInfo::CreateInstace, which calles AllocateArrayEx) - but
honestly. what's happening there is beyond my comprehension...

Klaus

Joe Feser said:
And if you change the code to
32000000

it will not throw an exception.

this does not allow you to just let the MemoryStream just do it's thing.

Just pointing this out to show it's not the machine.

Over 800mb are allocated with the ms.Capacity = 16776704 call.

Joe

Joe Feser said:
This very simple code throws an error on an XP Box running sp1 and all the
latest hot fixes.

All the memory in the machine is allocated and you must exit the
application.

try
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
ms.Capacity = 16776704;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}


"System.OutOfMemoryException: Exception of type System.OutOfMemoryException
was thrown."

Any clue?

It can also be reproduced if you keep writing a 512 byte array into the
MemoryStream until ensureCapacity tries to set the internal byte[]
length
to
16776704



Joe Feser
 
I know but as I was saying, if you just keep appending a 512 byte array,
when the object goes to allocate the 16mb array, it blows up.

I had to put a special case in my loop looking for the Capacity of the
MemoryStream.

This is a hack but did fix the problem for now.

Hopefully it can be fixed.

Did you reproduce it on another OS?

I know it was strange that only at 16mb range did it blow and not anything
bigger, like 32,000,000 or even 24,000,000

It could be an allocation issue, what is really wild is the fact every bit
of memory is taken by the system, all in the matter of 1/100 of a second,
the line spikes in Windows task manager.

Lets hope someone can shed some light on this one, I am shocked it has not
come up more often.

--
Joe Feser



Klaus Löffelmann said:
Joe,

it's not the problem of MemoryStream, which just occupies the given amount
of bytes in a byte-array. It's a problem of System.Array. Try to allocate a
byte (or whatever array is derived from System.Array) -array, it will
produce the same error. Try to allocate it with 32.000.000, it's running
fine.

Weird.

In my opinion it's a bug in the internal handler of System.Array (With
Rotor, I tried to track it down to InternalCreate which resolves in
COMArrayInfo::CreateInstace, which calles AllocateArrayEx) - but
honestly. what's happening there is beyond my comprehension...

Klaus

Joe Feser said:
And if you change the code to
32000000

it will not throw an exception.

this does not allow you to just let the MemoryStream just do it's thing.

Just pointing this out to show it's not the machine.

Over 800mb are allocated with the ms.Capacity = 16776704 call.

Joe

Joe Feser said:
This very simple code throws an error on an XP Box running sp1 and all the
latest hot fixes.

All the memory in the machine is allocated and you must exit the
application.

try
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
ms.Capacity = 16776704;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}


"System.OutOfMemoryException: Exception of type System.OutOfMemoryException
was thrown."

Any clue?

It can also be reproduced if you keep writing a 512 byte array into the
MemoryStream until ensureCapacity tries to set the internal byte[]
length
to
16776704



Joe Feser
 
Based on my test, the critical value is 16777197 (close to 16M). When
Capacity is set to less than this value, there will be a outofmemory
exception. I tested it with Windows Server 2003 and VS.NET 2003. This may
be an issue in arithmetic to allocate memory and build the buffer.


Luke
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 
Hello Joe,

Joe Feser said:
I know but as I was saying, if you just keep appending a 512 byte array,
when the object goes to allocate the 16mb array, it blows up.

I think that is because internally when extending the memory stream a
complete new byte array is created, which at a certain time exceeds the
"critical point". See source of rotor of the memory stream:

public virtual int Capacity {
get {
if (!_isOpen) __Error.StreamIsClosed();
return _capacity - _origin;
}
set {
if (!_isOpen) __Error.StreamIsClosed();
if (value != _capacity) {
if (!_expandable) __Error.MemoryStreamNotExpandable();
if (value < _length) throw new
ArgumentOutOfRangeException("value",
Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));

if (value > 0) {
byte[] newBuffer = new byte[value];
if (_length > 0) Buffer.InternalBlockCopy(_buffer, 0,
newBuffer, 0, _length);
_buffer = newBuffer;
}
else {
_buffer = null;
}

_capacity = value;

}
}
}
I had to put a special case in my loop looking for the Capacity of the
MemoryStream.

This is a hack but did fix the problem for now.

Hopefully it can be fixed.

Did you reproduce it on another OS?

I know it was strange that only at 16mb range did it blow and not anything
bigger, like 32,000,000 or even 24,000,000

It could be an allocation issue, what is really wild is the fact every bit
of memory is taken by the system, all in the matter of 1/100 of a second,
the line spikes in Windows task manager.

Lets hope someone can shed some light on this one, I am shocked it has not
come up more often.

Yeah, same wondering here...

Greetings from f.. cold again Germany
(I don't smoke inside, I know what I'm talking about...)

;-)

Klaus

Klaus Löffelmann said:
Joe,

it's not the problem of MemoryStream, which just occupies the given amount
of bytes in a byte-array. It's a problem of System.Array. Try to
allocate
a
byte (or whatever array is derived from System.Array) -array, it will
produce the same error. Try to allocate it with 32.000.000, it's running
fine.

Weird.

In my opinion it's a bug in the internal handler of System.Array (With
Rotor, I tried to track it down to InternalCreate which resolves in
COMArrayInfo::CreateInstace, which calles AllocateArrayEx) - but
honestly. what's happening there is beyond my comprehension...

Klaus

Joe Feser said:
And if you change the code to
32000000

it will not throw an exception.

this does not allow you to just let the MemoryStream just do it's thing.

Just pointing this out to show it's not the machine.

Over 800mb are allocated with the ms.Capacity = 16776704 call.

Joe

This very simple code throws an error on an XP Box running sp1 and
all
the
latest hot fixes.

All the memory in the machine is allocated and you must exit the
application.

try
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
ms.Capacity = 16776704;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}


"System.OutOfMemoryException: Exception of type
System.OutOfMemoryException
was thrown."

Any clue?

It can also be reproduced if you keep writing a 512 byte array into the
MemoryStream until ensureCapacity tries to set the internal byte[] length
to
16776704



Joe Feser
 
I have no problem with that, and I use Buffer.BlockCopy all the time.



It just seems weird that only at 16mb does it die, or 16384 adds of a
512byte array to a memory stream.



I added this hopefully temporary code to my loop that looks for a specific
size Stream and resizes it to 12000000



It runs great, so there is some magic behind the scenes that causes the
machine to suck every bit of memory from the machine when allocating the
16776704 byte array.



if (contents.Capacity > 8000000 && contents.Capacity < 9000000)



contents.Capacity = 12000000; //This will ensure a byte[] of 16776704 is
never created.



The code above keeps the exception from occurring.



What can I do.



Joe



Klaus Löffelmann said:
Hello Joe,

Joe Feser said:
I know but as I was saying, if you just keep appending a 512 byte array,
when the object goes to allocate the 16mb array, it blows up.

I think that is because internally when extending the memory stream a
complete new byte array is created, which at a certain time exceeds the
"critical point". See source of rotor of the memory stream:

public virtual int Capacity {
get {
if (!_isOpen) __Error.StreamIsClosed();
return _capacity - _origin;
}
set {
if (!_isOpen) __Error.StreamIsClosed();
if (value != _capacity) {
if (!_expandable) __Error.MemoryStreamNotExpandable();
if (value < _length) throw new
ArgumentOutOfRangeException("value",
Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));

if (value > 0) {
byte[] newBuffer = new byte[value];
if (_length > 0) Buffer.InternalBlockCopy(_buffer, 0,
newBuffer, 0, _length);
_buffer = newBuffer;
}
else {
_buffer = null;
}

_capacity = value;

}
}
}
I had to put a special case in my loop looking for the Capacity of the
MemoryStream.

This is a hack but did fix the problem for now.

Hopefully it can be fixed.

Did you reproduce it on another OS?

I know it was strange that only at 16mb range did it blow and not anything
bigger, like 32,000,000 or even 24,000,000

It could be an allocation issue, what is really wild is the fact every bit
of memory is taken by the system, all in the matter of 1/100 of a second,
the line spikes in Windows task manager.

Lets hope someone can shed some light on this one, I am shocked it has not
come up more often.

Yeah, same wondering here...

Greetings from f.. cold again Germany
(I don't smoke inside, I know what I'm talking about...)

;-)

Klaus

Klaus Löffelmann said:
Joe,

it's not the problem of MemoryStream, which just occupies the given amount
of bytes in a byte-array. It's a problem of System.Array. Try to
allocate
a
byte (or whatever array is derived from System.Array) -array, it will
produce the same error. Try to allocate it with 32.000.000, it's running
fine.

Weird.

In my opinion it's a bug in the internal handler of System.Array (With
Rotor, I tried to track it down to InternalCreate which resolves in
COMArrayInfo::CreateInstace, which calles AllocateArrayEx) - but
honestly. what's happening there is beyond my comprehension...

Klaus

And if you change the code to
32000000

it will not throw an exception.

this does not allow you to just let the MemoryStream just do it's thing.

Just pointing this out to show it's not the machine.

Over 800mb are allocated with the ms.Capacity = 16776704 call.

Joe

This very simple code throws an error on an XP Box running sp1 and all
the
latest hot fixes.

All the memory in the machine is allocated and you must exit the
application.

try
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
ms.Capacity = 16776704;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}


"System.OutOfMemoryException: Exception of type
System.OutOfMemoryException
was thrown."

Any clue?

It can also be reproduced if you keep writing a 512 byte array
into
the
MemoryStream until ensureCapacity tries to set the internal byte[]
length
to
16776704



Joe Feser
 
So does that mean you could reproduce it on a platform other than XP?

Is this a bug then?

Why do you think an allocation of 33553408 sucks less of the system memory
but still reports an OOM Exception?

About 200mb less is allocated by a SetCapacity of 33553408 than 16777197

and if you allocate a whole number like 32000000 or 24000000 it works great.

Do you need my MSDN Number or should I call product support?

Joe Feser
 
Here's the code for AllocateArrayEx.


http://www.slink-software.com/W/SL_...FILE_clr/src/vm/gcscan.cpp/L_500/LN_456#L_453



It seems that there are some logging code for logging code inside. If
you can find a version (debug/profile version?) with logging turn on,
maybe you can profile the pattern of the memory internal from the log
info. I debugged other large project with memory allocation issue
similar to this one. The issues is normallly relate to interaction
between a custom memory allocator that doesn't work well with certain
pattern of malloc call which can cause huge spike in the memory usage
in very very short time.


#ifdef _LOGALLOC
#ifdef LOGGING
if (LoggingOn(LF_GCALLOC, LL_INFO10))
{
CQuickBytes qb;
LPSTR classname = (LPSTR) qb.Alloc(MAX_CLASSNAME_LENGTH *
sizeof(CHAR));
arrayDesc->GetElementTypeHandle().GetName(classname,
MAX_CLASSNAME_LENGTH * sizeof(CHAR));

LogSpewAlways("Allocated %5d bytes for %s_TYPE" FMT_ADDR
"%s[]\n",
totalSize,
pArrayMT->GetClass()->IsValueClass() ? "VAL"
: "REF",
DBG_ADDR(orObject), classname);

if (LoggingOn(LF_GCALLOC, LL_INFO100000) ||
(LoggingOn(LF_GCALLOC, LL_INFO100) &&
ToLogOrNotToLog(totalSize, classname)))
{
void LogStackTrace();
LogStackTrace();
}
}
#endif
Klaus Löffelmann said:
Joe,

it's not the problem of MemoryStream, which just occupies the given amount
of bytes in a byte-array. It's a problem of System.Array. Try to allocate a
byte (or whatever array is derived from System.Array) -array, it will
produce the same error. Try to allocate it with 32.000.000, it's running
fine.

Weird.

In my opinion it's a bug in the internal handler of System.Array (With
Rotor, I tried to track it down to InternalCreate which resolves in
COMArrayInfo::CreateInstace, which calles AllocateArrayEx) - but
honestly. what's happening there is beyond my comprehension...

Klaus

Joe Feser said:
And if you change the code to
32000000

it will not throw an exception.

this does not allow you to just let the MemoryStream just do it's thing.

Just pointing this out to show it's not the machine.

Over 800mb are allocated with the ms.Capacity = 16776704 call.

Joe

Joe Feser said:
This very simple code throws an error on an XP Box running sp1 and all the
latest hot fixes.

All the memory in the machine is allocated and you must exit the
application.

try
{
System.IO.MemoryStream ms = new System.IO.MemoryStream();
ms.Capacity = 16776704;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}


"System.OutOfMemoryException: Exception of type System.OutOfMemoryException
was thrown."

Any clue?

It can also be reproduced if you keep writing a 512 byte array into the
MemoryStream until ensureCapacity tries to set the internal byte[]
length
to
16776704



Joe Feser
 
Hi Joe,

With some efforts, I have confirmed this is a bug in .NET framework 1.1.
When the value of Capacity is in the range of > 16773044 && < 16777197(16
MB), the OutofMemoryException is raised. This has been fixed in Framework
1.1 Sp1.

Luke
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
 
Joe,
This very simple code throws an error on an XP Box running sp1 and all the
latest hot fixes.

I believe there's a hotfix available for this bug as well, but you
have to call Microsoft Support to get it.



Mattias
 
Out of Memory Exception

Hi,

Are you sure it is 16MB?

I'm working on a out of memory exception - And the code is written in .net framework 1.1

The code initializes the memory stream to say 10MB and then tries to load a large xml file - 200MB. When it loads the file it gives me an out of memory exception

Dim ms as new MemoryStream(1048576*10)

The code also prints to a file the memory Stream length at a particular point in the code

15:54:58 > ResponseWorker : ResponseMessage : Failed : Transformer : Exception of type System.OutOfMemoryException was thrown.
15:55:09 > Loader : ImportXML : Memory Stream Length 167771139
15:56:25 > Loader : ImportXML : Exception Occurred.

and the length here appears to be 167 MB.

and subsequently if i reinitialize the memory stream to
Dim ms as new MemoryStream(1048576*50)

i dont seem to get any error. Not able to reason it though...


Cheers
Manju
 
Back
Top