2

I have encountered a strange NullPointerException when invoking method AppendLine() on a StringBuilder at .NET Framework 4.5.2.

The exception trace is shown as below:

System.NullReferenceException: NullReferenceException Object reference not set to an instance of an object.
at System.Text.StringBuilder.Append(String value)
at System.Text.StringBuilder.AppendLine(String value)
......

And the dump of application is shown as follow:

enter image description here

It is clearly that the exception is caused due to field m_ChunkChars of StringBuilder.

Also, there is NO concurrent or reflection operations in relevant context.

I have attempted to analysis the source code of StringBuilder, but eventually failed to find any possible reasons.

Could anyone give any thoughts about possible conditions that may lead to such situation?

public void SomeLocalMethod()
{
    var log = new StringBuilder();
    //some operations
    log.AppendLine("Some log text.")
        .Append("Some log")
        .AppendLine("Some log");
}

The code is quite simple. We constructed a StringBuilder in a method to log several operations.

The reason why I think it is not a thread-safe problem is that this StringBuilder is a local variable and no reference is passed outside of its method.

Also, we found on system logs that such errors are continuous and frequent on multiply nodes, which I think implies it is possibly not caused by unexpected access during object initialization.

The code is run at .NET framework 4.5.2 again.

Chris
  • 339
  • 4
  • 17
  • 1
    Could we start seeing the input you have given to the StringBuilder instance? – Steve May 26 '21 at 12:15
  • 5
    What is the code causing the problem? –  May 26 '21 at 12:22
  • @OlivierRogier That question is obviously unrelated – canton7 May 26 '21 at 12:26
  • 1
    We will need to see some actual code, if there was a serious bug in StringBuilder it would be known and fixed, so most likely we have something strange going on. – Lasse V. Karlsen May 26 '21 at 12:26
  • Do you have any code anywhere which sets individual chars of the StringBuilder? E.g. `sb[10] = 'x'` – canton7 May 26 '21 at 12:30
  • The only place I can see where `m_ChunkChars` gets set to `null` is when an OutOfMemoryException occurs, in which case that OOM should bubble up and crash you. If you have multi-threaded access you might get an NRE before the OOM occurs, or maybe you're catching the OOM? Either way, this should only happen if you get an SB with more than 3gigs or so of data... – canton7 May 26 '21 at 12:34
  • @canton7 no, all we did is constructing a StringBuilder and append some lines to it in a local method. – Chris May 26 '21 at 12:35
  • 1
    The code you have posted runs fine. Show us the **real** code! – Olivier Jacot-Descombes May 26 '21 at 12:38
  • 2
    As expected, using the code posted, no exception happens – Steve May 26 '21 at 12:38
  • Chill out guys. OP obviously isn't lying about the NRE, see the dump. The call stack they posted shows that it is a call to `StringBuilder.AppendLine` which is failing. – canton7 May 26 '21 at 12:39
  • Your first sentence says NullPointerException spelling error or something else? – Rand Random May 26 '21 at 12:43
  • @Chris The code provided works fine and is not the problem. You can try to [repair](https://www.microsoft.com/en-us/download/details.aspx?id=30135) or reinstall the .NET Environment, or as a last resort the operating system. What happens on another computer, if you can try? I *don't know* this tool: https://www.majorgeeks.com/files/details/microsoft_net_framework_cleanup_tool.html –  May 26 '21 at 12:45
  • 1
    The extreme value of m_ChunkOffset (The logial offset = sum of all characters in previous blocks) indicates that the string is already very, very long. So either an out-of-memory situation or some sort of data corruption. – Klaus Gütter May 26 '21 at 12:45
  • The code has been run in production environment for years on physical machines with 192G memory. However, the error occurs when we tried to run it in dockers with 24G memory. The memory usage is in a higher level than before, but no oom was found in log tho. The StringBuilder contains only about 20 characters. The NPE did not crash the program, all other function works well except NPE keeps showing up at logs. – Chris May 26 '21 at 12:51
  • Maybe naive thought, but when you had no issues with 192G memory and now you have problems with 24G it sounds like oom, or doesn't it? – Rand Random May 26 '21 at 12:55
  • `The StringBuilder contains only about 20 characters` - I doubt that. The dump you showed proves different. Are there other places in your code which could create such huge StringBuilder instances? The content "IncRefreshInterval" etc. could be a hint. – Klaus Gütter May 26 '21 at 12:58
  • @RandRandom We load less cache data in each docker but deploy more dockers to run the application. – Chris May 26 '21 at 12:59
  • Maybe try to set `MaxCapacity` for every `StringBuilder` instance to find the error? - https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.maxcapacity - https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.-ctor – Rand Random May 26 '21 at 13:01
  • The memory of the system doesn't matter. StringBuilder cannot hold more than int.MaxValue chars regardless of the amount of available memory. You've got about 2.14GB to play with per StringBuilder, and that's all. – canton7 May 26 '21 at 13:02
  • @canton7 - and the idea it may overflow is because `m_chunkOffset` is `2147481925` am I reading the dump correctly? – Rand Random May 26 '21 at 13:03
  • Yes. If you try and append to the StringBuilder, and it needs to add another chunk, and adding the new chunk size to `m_ChunkOffset` overflows an int, then you fail the test on [this line](https://referencesource.microsoft.com/#mscorlib/system/text/stringbuilder.cs,1985) and `m_ChunkChars` gets set to `null`. You'll also get an `OutOfMemoryException`, but OP is presumably swallowing that (although they're not showing their actual code). Since `m_ChunkOffset` is just 1700 or so bytes away from `int.MaxValue`, and chunks are ~8000, they presumably just hit that case – canton7 May 26 '21 at 13:06
  • So, 1) OP must be appending *lots* of data to this SB (although their sample code doesn't show this), and 2) They must be swallowing that OutOfMemoryException (although, again, their sample code doesn't show this) – canton7 May 26 '21 at 13:08
  • My guess would be that since the variable is called `log` it is used to log stuff, and then also used to log stuff in a catch block, but then it is already too late. – Lasse V. Karlsen May 26 '21 at 13:22
  • They said the SB is entirely a local variable, but perhaps it's being passed into other helper methods which are doing their own catching. @Chris, this is why seeing your actual code is important! – canton7 May 26 '21 at 13:23
  • 1
    Creating a *reliable* logger is a lot harder than it looks. The snippet nor the limited stack trace are useful to see how this went wrong, the object state tells the tale however. The very large m_ChunkOffset tell us that the string is very large and only 1723 chars could still be appended. The next Append() call tripped [this exception](https://referencesource.microsoft.com/#mscorlib/system/text/stringbuilder.cs,1985). Very nasty, but the code just kept motoring, possibly to log the exception. That was the end of it. – Hans Passant May 26 '21 at 13:40
  • Can you test this out on a newer version of .Net? Does this happen on other machines? – ΩmegaMan May 26 '21 at 13:59
  • @canton7 I sincerely appreciate your help and time, and it is true that the largest possibility would just be what you have pointed out. Sorry that I cannot provide the real code as it violates the codes of my company. I will continue to save this problem. Thank you again. – Chris May 26 '21 at 14:04
  • 3
    @Chris This is why [ask] asks you to create a [mcve]. At the very least, your posted code sample should attempt to capture the complexity of your actual code. It's not just a tick-box exercise where you can paste the minimum to keep those annoying answerers happy: it's there to help us work out whether particular explanations are consistent with your code. – canton7 May 26 '21 at 14:29

0 Answers0