Problem:
Here is a logical depiction of the buffer pools from above example. The ‘n’ below represents the number of buffers each pool can hold.
So, for all practical purposes, n => MaxBufferPoolSize / buffer size that pool can hold.
Say, the MaxBufferPoolSize for the above example is 1800.
Note that each buffer pool’s maximum size is the same, though the sizes of the buffers being held by each of those differ.
1. Request
arrives
2. BufferManager tries to find a pool that can hold buffers that are sufficient for this message
3. If appropriate pool is identified with available buffers, the message gets processed. Otherwise, the
Developing or consuming service-oriented applications? Ever
came across HTTP 400’s? …a needless question though. It is not uncommon to find
a WCF CommunicationException with HTTP 400 (Bad Request). Quite a few of these
instances may happen to be returning an error message that would say – “The maximum message size quota for incoming
messages (65536) has been exceeded. To increase the quota, use the
MaxReceivedMessageSize property on the appropriate binding element”.
In certain complex
project architectures, you may not get to see the detailed error message,
especially, if you are just working on the UI end of it.
To find out a
solution for this, let us understand how WCF does the buffer management.
Background & Solution:
What?
In the simplest
form, a buffer is a chunk of memory and buffer pool is a set of one or more
buffers.
It all starts when
the BufferManager is created. BufferManager creates and manages the buffers and
buffer pools, depending on the values that you set in the binding configuration
in the application’s config file. Let’s first see what those buffer-related
values are.
MaxBufferPoolSize
is the size of the largest buffer pool that the BufferManager can manage.
·
If, in
case, allocating buffer to an incoming message would mean that it would exceed
this limit, then, the message would take a new buffer out of the heap and the
garbage collector takes care of it once used up.
·
If this
value is 0(zero), it means that each request would take a new buffer out of the
memory heap and will be taken care of, by the garbage collector.
MaxBufferSize is
the size of the largest individual buffer that the buffer manager can allocate
(to any buffer pool).
For buffered transfers, this is the same as the MaxBufferSize.
- For streamed transfers, only the SOAP headers need to be buffered while the message body can be streamed on-demand. In such cases, this would just mean the maximum size of the SOAP headers. For buffered transfers, it is the maximum size of the headers and message body.
For buffered transfers, this is the same as the MaxBufferSize.
- For streamed transfers, this is either greater than or equal to MaxBufferSize (for obvious reasons mentioned above)
How?
When the
BufferManager is created, a series of buffer pools are also created, with each
pool holding up to a memory determined by MaxBufferPoolSize.
The buffers in each
of these pools would have sizes in an incremental pattern of the multiples of
128, until it reaches the maximum allowed message size (as set by MaxBufferSize).
For example, if the
MaxBufferSize is set to 450 bytes, the BufferManager would create 3 pools as –
1st pool
that holds buffers of size 128 bytes each
2nd pool that holds buffers of size 256 bytes each
3rd pool that holds buffers of size 450 bytes (this pool should otherwise hold buffers of size 512, but since the MaxBufferSize is lesser than 512, it would limit it to 450)
2nd pool that holds buffers of size 256 bytes each
3rd pool that holds buffers of size 450 bytes (this pool should otherwise hold buffers of size 512, but since the MaxBufferSize is lesser than 512, it would limit it to 450)
Here is a logical depiction of the buffer pools from above example. The ‘n’ below represents the number of buffers each pool can hold.
So, for all practical purposes, n => MaxBufferPoolSize / buffer size that pool can hold.
Say, the MaxBufferPoolSize for the above example is 1800.
Note that each buffer pool’s maximum size is the same, though the sizes of the buffers being held by each of those differ.
When?
Buffers are not
allocated when the pools are created. They are only allocated when a message
arrives to be processed.
2. BufferManager tries to find a pool that can hold buffers that are sufficient for this message
3. If appropriate pool is identified with available buffers, the message gets processed. Otherwise, the
message is allocated a buffer out of the heap (not
managed by BufferManager).
4. Also, If the message size is more than the MaxBufferSize, it is allocated an unmanaged buffer, on the
4. Also, If the message size is more than the MaxBufferSize, it is allocated an unmanaged buffer, on the
memory heap i.e., buffer that is garbage collected as when it is
available for collection.
5. Once processed, the buffer manager tries to put the buffer that held the message back into its pool for
5. Once processed, the buffer manager tries to put the buffer that held the message back into its pool for
reuse. However, if by doing so, the pool’s size exceeds
the MaxBufferPoolSize, the buffer will not be
returned back to the pool.
Here is more detailed write-up on WCF Memory Buffer Management by Andrew.
Please comment if this served your purpose by any chance. Happy coding :)
Trade-off
If you need to
process large messages, you can use a large MaxBufferSize. But, using too much
memory would also decrement performance. As a developer, you should find a
balance between time and memory.
As a thumb rule, if your server, normally has ‘n’ number of concurrent requests, then you may have to set the MaxBufferPoolSize as ‘n’ times the MaxBufferSize.
Example configuration
Finally, let us see
a sample configuration setting w.r.t. the settings we defined in the "What?" section of this post.
<binding name="WSHttpBinding_SampleFeedService" closeTimeout="00:00:30"
openTimeout="00:00:30"
receiveTimeout="00:00:30" sendTimeout="00:00:30" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="2097152" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas
maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security
mode="None">
<transport
clientCredentialType="Windows" proxyCredentialType="None" realm="" />
<message
clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true" />
</security>
</binding>
Here is more detailed write-up on WCF Memory Buffer Management by Andrew.
Please comment if this served your purpose by any chance. Happy coding :)
Thanks for posting this.
ReplyDeletewe are facing very weird issue- to allow large message transfer we had set the maxBufferPoolSize= 2GB but we found that very large buffers are created by the bufferPool (68MB,128MB) we started getting OutOfMemory exception.
To avoid that we decided to reduce maxBufferPoolSize = 2MB.but still we found single very large buffer(68MB/128MB) is created & hold long time by some other channel related classe.
may I request yo to please help us?
With profiling I found that for allocations like 35 MB it unnecessarily tries to allocate 68MB buffer which is really huge and it creates problems as OutOfMemory
DeleteSo basically i want to get rid of holding of larger buffers for message size > certain MBs so that service should not hog up large memory.
Hello Aniket,
DeleteApologies for not getting back to you quickly. I was down with viral fever for almost 4 days :(
Anyways, coming to the case described in your comments -
I would say the move to reduce maxBufferPoolSize to 2MB is a sensible one and it should have solved your purpose.
But, Have you made sure that the same change has been done on the service side configuration too? I mean the config settings on the service side. Usually, both the consumer config values and the config values on the service-side should match.
For my better understanding, can you please post your actual config settings, just the elements (like as I mentioned the sample setting in my post above). It would be good if you can post settings from both ends - consuming end setting and service end setting.
yes, that's what we thought of-like reducing maxBufferPoolSize to 5MB. After doing that BufferPoolManager stopped allocating buffers of different sizes.But I found weird behavior when service send 30MB response then I can see 30MB byte arr in heap with no reference(i.e. can be Gc'ed) but when after some time Service send another 1MB response then two allocations are seen.
Delete1MB - no reference - can be GC'ed
30MB - Referenecd by multiple objects.Prevents from GC collection.
Even after service sends couple of 1MB callbacks the 30MB array remains over there.
This behavior can be easily reproduced with default pipe binding parameters with buffering enabled. I tried with 0 as maxBufferPoolSize and that too on both client and service side(see the following binding)
EndpointAddress endPointAddress = new EndpointAddress(serviceAddress);
NetNamedPipeBinding netNamedPipeBinding = new NetNamedPipeBinding();
//Set maxbufferPoolSize,maxBufferSize,maxReceivedMessageSize
netNamedPipeBinding.MaxBufferPoolSize = 0;
netNamedPipeBinding.MaxBufferSize = 1 * 1024 * 1024 * 1024;
netNamedPipeBinding.MaxReceivedMessageSize = 1 * 1024 * 1024 * 1024;
Have you been thinking about the power sources and the tiles whom use blocks I wanted to thank you for this great read!! I definitely enjoyed every little bit of it and I have you bookmarked to check out the new stuff you post
ReplyDeleteData Science Training in Indira nagar
Data Science training in marathahalli
Data Science Interview questions and answers
Data Science training in btm layout
Data Science Training in BTM Layout
Data science training in kalyan nagar
Very nice post here and thanks for it .I always like and such a super contents of these post.Excellent and very cool idea and great content of different kinds of the valuable information's.
ReplyDeleterpa training in bangalore
best rpa training in bangalore
rpa training in pune | rpa course in bangalore
rpa training in chennai
Great post! Your insights are spot-on. I appreciate how you break down intricate subjects, making them accessible and understandable.
ReplyDeleteBest Software Training Institute In Electronic City Bangalore