Floris Robbemont Lead Developer - Dutch Grit 3 december 2018

Het reviewen van code en zoeken naar verbeteringen is onderdeel van mijn rol als technical lead in het Dutch Grit team. Voor onze eigen projecten is het al langere tijd de gewoonte om code te reviewen. Sinds kort bieden we dit ook aan voor collega developers. In deze blog een bevinding met betrekking tot het gebruik van .ToLower() wanneer twee strings met elkaar worden vergeleken. Alhoewel dit in principe niet ‘fout’ is, en het zeker het gewenste resultaat zal leveren, kleven er wel nadelen aan deze manier.


Elke keer als je op een string .ToLower() aanroept wordt er een nieuwe string in geheugen gemaakt (strings zijn in .NET namelijk immutable). Bij een vergelijking van twee strings:

tolower image 1

worden twee nieuwe strings aangemaakt in geheugen. Geheugen in .NET wordt beheerd door het framework zelf, en opgeruimd door een systeem genaamd ‘Garbage collector’. De garbage collector scanned het geheugen van je applicatie continu om dingen te ontdekken die opgeruimd kunnen worden. De twee nieuwe strings uit het bovenstaande voorbeeld zullen door de garbage collector worden opgepakt en worden opgeruimd.

Het scannen van de applicatie kan via een background thread gebeuren, maar af en toe is het nodig om de hele applicatie te pauzeren (https://www.jetbrains.com/help/profiler/CLR_Activity.html). Soms worden ook individuele threads geblokkeerd. De garbage collector in .NET is extreem efficiënt, en zal altijd op zoek gaan naar mogelijkheden die het minste performance zullen vreten. Maar, net zoals het afval thuis, zal het toch een keer opgeruimd moeten worden.

Een ander probleem is geheugen fragmentatie. Een blokje geheugen van 1KB kan, zodra er een blokje achter wordt gezet, nooit meer groter worden (zelfde probleem vind plaats op de harde schijf). Wanneer je vervolgens een blokje van 1.2 KB wilt alloceren zal het achteraan geplaatst moeten worden.
Je hebt dan technisch gezien geen memory leak in de applicatie, maar toch zal het gebruikte geheugen blijven groeien. Tot het moment dat het niet meer kan groeien en blocking garbage collection cycles plaats zullen vinden.

Dit zijn de twee bekendste problemen. Andere problemen zijn dat ToLower() en ToUpper() cultuur afhankelijk zijn. In de Turkse cultuur is de kleine letter i zonder punt. Dit veroorzaakt grote problemen.

.NET komt met een betere oplossing: .Equals(). Die methode bevat een overload wanneer je een cultuur instelling mee kan sturen. Bovenstaand voorbeeld wordt dan:

ToLower image2


Binnen de Equals methode vinden geen verdere string allocaties plaats, waardoor dit een geheugen vriendelijke optie is. Als je weet dat de thread waarin je dit uitvoert verschillende culturen kan hebben kun je gebruik maken van StringComparison.InvariantCultureIgnoreCase.

Eigenlijk zijn er maar twee situatie waarin .Tolower() en .ToUpper() gebruikt hoeven worden:

  • In een switch case
  • Wanneer je een unieke sleutel moet maken *

*) Let op dat bij het maken van bijvoorbeeld een Dictionary een overload bestaat waarbij je een IEqualityComparer<> kunt opgeven van het type van de sleutel. Het .NET framework komt standaard met een aantal ingebouwde comparers die je hiervoor kan gebruiken. Zo kun je ook bij dictionaries een entry ophalen met een sleutel zonder hoofdletter gevoeligheid.

ToLower Image3


Heb je vragen of wil je meer weten over geheugen management binnen .NET? Neem gerust contact met me op.

Back