{"id":54278,"date":"2025-12-01T05:17:58","date_gmt":"2025-12-01T05:17:58","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=54278"},"modified":"2025-12-01T05:17:58","modified_gmt":"2025-12-01T05:17:58","slug":"dotnet-memory-optimization-in-net-with-object-pooling","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/dotnet-memory-optimization-in-net-with-object-pooling\/","title":{"rendered":"DOTNET: Memory Optimization in .NET with Object Pooling"},"content":{"rendered":"\n<p>Here\u2019s a <strong>single, self-contained console app<\/strong> you can run to <em>see and feel<\/em> the difference between:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83d\udeab <strong>Without Object Pooling<\/strong> (allocate new arrays every time)<\/li>\n\n\n\n<li>\u2705 <strong>With Object Pooling<\/strong> (reuse arrays via <code>ArrayPool&lt;byte><\/code>)<\/li>\n<\/ul>\n\n\n\n<p>It includes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Full <strong><code>Program.cs<\/code> code<\/strong><\/li>\n\n\n\n<li>Optional <strong><code>.csproj<\/code><\/strong> (if you want a drop-in project)<\/li>\n\n\n\n<li>Step-by-step <strong>how to run<\/strong><\/li>\n\n\n\n<li><strong>How to interpret<\/strong> the results<\/li>\n<\/ul>\n\n\n\n<p>Works on any modern .NET (7\/8\/9\/10+).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1\ufe0f\u20e3 Full Code \u2013 <code>Program.cs<\/code><\/h2>\n\n\n\n<script src=\"https:\/\/gist.github.com\/devops-school\/b5b348d9f43cafc4f1732b26fbb46c3f.js\"><\/script>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\"><\/code><\/span><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2\ufe0f\u20e3 Optional \u2013 Minimal <code>.csproj<\/code> (if you want full project file)<\/h2>\n\n\n\n<p>Create a file named <code>ObjectPoolingDemo.csproj<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Project<\/span> <span class=\"hljs-attr\">Sdk<\/span>=<span class=\"hljs-string\">\"Microsoft.NET.Sdk\"<\/span>&gt;<\/span>\n\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PropertyGroup<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">OutputType<\/span>&gt;<\/span>Exe<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">OutputType<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TargetFramework<\/span>&gt;<\/span>net8.0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">TargetFramework<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ImplicitUsings<\/span>&gt;<\/span>enable<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ImplicitUsings<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Nullable<\/span>&gt;<\/span>enable<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Nullable<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">PropertyGroup<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Project<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>You can also change <code>net8.0<\/code> to <code>net9.0<\/code> or later (e.g., <code>net10.0<\/code>) depending on your installed SDK.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3\ufe0f\u20e3 Step-by-Step: How to Run This Demo<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1 \u2013 Make a new console project<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">dotnet <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">console<\/span> -n ObjectPoolingDemo\ncd ObjectPoolingDemo\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 2 \u2013 Replace <code>Program.cs<\/code><\/h3>\n\n\n\n<p>Open <code>Program.cs<\/code> in your editor and <strong>replace everything<\/strong> with the code from section <strong>1\ufe0f\u20e3<\/strong>.<\/p>\n\n\n\n<p>(Optionally replace the autogenerated <code>.csproj<\/code> with the one in <strong>2\ufe0f\u20e3<\/strong>, but not required.)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3 \u2013 Build and run in Release<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">dotnet run -c Release\n<\/code><\/span><\/pre>\n\n\n<p>You\u2019ll see output like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">=========================================\n   Object Pooling Demo (ArrayPool<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">byte<\/span>&gt;<\/span>)  \n=========================================\n\nIterations : 1,000,000\nBufferSize : 1024 bytes\n\nWarming up JIT (small runs)...\n--- Warmup - Without Pooling ---\nIterations: 100,000, BufferSize: 1024 bytes\nTime Elapsed : 120 ms\nGC Gen0      : 12\nGC Gen1      : 1\nGC Gen2      : 0\nManaged Memory Delta (approx): 3.40 MB\nChecksum (ignore, just to keep JIT honest): 123456789\n\n--- Warmup - With Pooling ---\nIterations: 100,000, BufferSize: 1024 bytes\nTime Elapsed : 60 ms\nGC Gen0      : 2\nGC Gen1      : 0\nGC Gen2      : 0\nManaged Memory Delta (approx): 0.20 MB\nChecksum (ignore, just to keep JIT honest): 123456789\n\n=========== REAL TESTS (Release) ==========\n\n--- WITHOUT pooling (new byte&#91;] each time) ---\nIterations: 1,000,000, BufferSize: 1024 bytes\nTime Elapsed : 900 ms\nGC Gen0      : 100\nGC Gen1      : 5\nGC Gen2      : 1\nManaged Memory Delta (approx): 40.00 MB\nChecksum (ignore, just to keep JIT honest): 123456789\n\n--- WITH pooling (ArrayPool<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">byte<\/span>&gt;<\/span>.Shared) ---\nIterations: 1,000,000, BufferSize: 1024 bytes\nTime Elapsed : 400 ms\nGC Gen0      : 5\nGC Gen1      : 0\nGC Gen2      : 0\nManaged Memory Delta (approx): 2.00 MB\nChecksum (ignore, just to keep JIT honest): 123456789\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>(Exact numbers will differ per machine, but the <strong>pattern<\/strong> will be similar.)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">4\ufe0f\u20e3 How to \u201cExperience\u201d and Interpret the Results<\/h2>\n\n\n\n<p>Focus on these lines for each scenario:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Time Elapsed<\/strong><\/li>\n\n\n\n<li><strong>GC Gen0 \/ Gen1 \/ Gen2<\/strong><\/li>\n\n\n\n<li><strong>Managed Memory Delta (MB)<\/strong><\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd34 Scenario: WITHOUT pooling<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>new byte[bufferSize]<\/code> is executed <strong>1,000,000 times<\/strong>.<\/li>\n\n\n\n<li>That means ~1,024 * 1,000,000 \u2248 <strong>1 GB worth of arrays allocated over time<\/strong>.<\/li>\n\n\n\n<li>You should see:\n<ul class=\"wp-block-list\">\n<li><strong>Higher elapsed time<\/strong><\/li>\n\n\n\n<li><strong>Many more Gen0 collections<\/strong><\/li>\n\n\n\n<li>Possibly some Gen1\/Gen2 collections<\/li>\n\n\n\n<li>A larger <strong>memory delta<\/strong><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>This simulates a <strong>real-world high-allocation hot path<\/strong> (e.g., per-request\/per-message allocations).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udfe2 Scenario: WITH pooling (ArrayPool.Shared)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Arrays are rented and returned from a <strong>shared pool<\/strong>.<\/li>\n\n\n\n<li>Only a <strong>small number of underlying arrays<\/strong> are actually allocated.<\/li>\n\n\n\n<li>Subsequent rents <strong>reuse<\/strong> these buffers.<\/li>\n<\/ul>\n\n\n\n<p>Expected result:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Lower elapsed time<\/strong> (less GC interference + fewer allocations)<\/li>\n\n\n\n<li><strong>Much fewer Gen0 collections<\/strong>, often 5\u201310x fewer<\/li>\n\n\n\n<li>Gen1\/Gen2 collections may drop to <strong>0 or near 0<\/strong><\/li>\n\n\n\n<li><strong>Managed Memory Delta<\/strong> much smaller<\/li>\n<\/ul>\n\n\n\n<p>You are literally seeing <strong>GC pressure decreasing<\/strong> because:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Without pooling \u2192 allocate, discard, GC must clean up<\/li>\n\n\n\n<li>With pooling \u2192 allocate a few times, then reuse, GC is mostly idle<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5\ufe0f\u20e3 Tweaking the Demo to Feel the Effect More<\/h2>\n\n\n\n<p>If you want even more dramatic differences:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Increase <code>iterations<\/code> (e.g., <code>5_000_000<\/code>)<\/li>\n\n\n\n<li>Or increase <code>bufferSize<\/code> (e.g., <code>4096<\/code> or <code>16_384<\/code>)<\/li>\n<\/ul>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> int iterations = <span class=\"hljs-number\">5<\/span>_000_000;\n<span class=\"hljs-keyword\">const<\/span> int bufferSize = <span class=\"hljs-number\">4096<\/span>;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u26a0\ufe0f Be careful: this can make the <strong>non-pooled version quite heavy<\/strong> on CPU and memory. Good for a demo on a strong machine, but maybe too much on low-end hardware.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">6\ufe0f\u20e3 How to Understand in Training<\/h2>\n\n\n\n<p>You can summarize for your audience:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u201cIn the <strong>no pooling<\/strong> scenario, we allocate a <strong>fresh buffer for each iteration<\/strong>, forcing the GC to keep cleaning up.<br>In the <strong>pooling<\/strong> scenario, we reuse buffers from <code>ArrayPool&lt;byte&gt;.Shared<\/code>, dramatically reducing allocations and GC work.<br>The difference in <strong>GC counts<\/strong> and <strong>elapsed time<\/strong> is the direct impact of object pooling.\u201d<\/p>\n<\/blockquote>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here\u2019s a single, self-contained console app you can run to see and feel the difference between: It includes: Works on any modern .NET (7\/8\/9\/10+). 1\ufe0f\u20e3 Full Code \u2013 Program.cs 2\ufe0f\u20e3&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[11138],"tags":[],"class_list":["post-54278","post","type-post","status-publish","format-standard","hentry","category-best-tools"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54278","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=54278"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54278\/revisions"}],"predecessor-version":[{"id":54279,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54278\/revisions\/54279"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=54278"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=54278"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=54278"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}