Fallstricke im String-Pool oder nur ein weiterer Grund zum Nachdenken, bevor String-Instanzen in C # interniert werden

Als Softwareentwickler möchten wir immer, dass die von uns geschriebene Software schnell ausgeführt wird. Unter Verwendung des optimalen Algorithmus, Parallelisierung und Anwendung verschiedener Optimierungstechniken greifen wir auf alle uns bekannten Mittel zurück, um die Leistung der Software zu verbessern. Eine dieser Optimierungstechniken ist das sogenannte String Interning. Sie können damit den vom Prozess verbrauchten Speicherplatz reduzieren und den Zeitaufwand für den Vergleich von Zeichenfolgen erheblich reduzieren. Wie überall im Leben ist es jedoch notwendig, die Maßnahme zu beachten - Sie sollten nicht bei jedem Schritt Internierung anwenden. Der Rest dieses Artikels zeigt Ihnen, wie Sie sich selbst verbrennen und einen nicht offensichtlichen Engpass für Ihre Anwendung in Form der String.Intern-Methode erstellen können.





, , , C# . , – , , String, .





, , : , . . 100 %, . , 1 , , 4,7 ( 100 ). , , , , . , .





, String . -, ( String Pool). , . , , , . , , String. String, . , ( ). : String.Intern String.IsInterned.





, , String. , . IsInterned . , null ( ).





, , Intern. , , . , , , . , . , , .





. String.Equals:





public bool Equals(String value)
{
  if (this == null)
    throw new NullReferenceException();
 
  if (value == null)
    return false;
 
  if (Object.ReferenceEquals(this, value))
    return true;
  
  if (this.Length != value.Length)
    return false;
 
  return EqualsHelper(this, value);
}

      
      



EqualsHelper, , Object.ReferenceEquals . , Object.ReferenceEquals true ( ). , , EqualsHelper . Equals , , false ReferenceEquals .





, , Object.ReferenceEquals. . - , – . ReferenceEquals , .





, . , . .





, , , .





,

bug tracker' - , : ++ . , PVS-Studio . , , IncrediBuild. IncrediBuild , , . , , , , ( ), . .





, , PVS-Studio , IncrediBuild, , . – . , .





open source Unreal Tournament. IncrediBuild . 145 .





Unreal Tournament PVS-Studio, . CLMonitor.exe , Unreal Tournament Visual Studio. , , CLMonitor.exe, . PVS-Studio ThreadCount, CLMonitor.exe PVS-Studio.exe, ++ . PVS-Studio.exe , CLMonitor.exe.





: ThreadCount PVS-Studio, (145), , , 145 PVS-Studio.exe . IncrediBuild Build Monitor, , . - :





, : , , IncrediBuild . ...





,

, PVS-Studio.exe Build Monitor. IncrediBuild IncrediBuild. , , , : 182 8 50 IncrediBuild 145 . , 18 , 3,5 . Build Monitor. , :





, PVS-Studio.exe , - PVS-Studio.exe. . . . , , – IncrediBuild. - .





. , , . , CLMonitor.exe , . "" , CLMonitor.exe Visual Studio . Threads, 145 . , , :





....
return String.Intern(settings == null ? path
                                 : settings
                                 .TransformToRelative(path.Replace("/", "\\"),
                                                      solutionDirectory));
....
analyzedSourceFiles.Add( String.Intern(settings
                        .TransformPathToRelative(analyzedSourceFilePath, 
                                                 solutionDirectory))
                       );
....

      
      



? String.Intern. . , , CLMonitor.exe , PVS-Studio.exe. ErrorInfo, . , . , , ErrorInfo . .





, . , . - 145 String.Intern, LimitedConcurrencyLevelTaskScheduler CLMonitor.exe , PVS-Studio.exe, IncrediBuild . , , – PVS-Studio.exe ErrorInfo . , PVS-Studio.exe . , 145 .





ThreadCount , , String.Intern.





, CLMonitor.exe. : , PVS-Studio.exe ( , ).





, . Build Monitor - PVS-Studio.exe. 50 26, . , IncrediBuild 145 , 7 . , 3,5 .





String.Intern – , CoreCLR

, String.Intern, , - lock'. , String.Intern - , , , . , String.Intern reference source. , – Thread.GetDomain().GetOrInternString(str). , :





internal extern String GetOrInternString(String str);

      
      



. - . ? - CLR, .NET. , CoreCLR. , GetOrInternString :





STRINGREF *BaseDomain::GetOrInternString(STRINGREF *pString)

      
      



GetInternedString. , :





....
if (m_StringToEntryHashTable->GetValue(&StringData, &Data, dwHash))
{
  STRINGREF *pStrObj = NULL;
  pStrObj = ((StringLiteralEntry*)Data)->GetStringObject();
  _ASSERTE(!bAddIfNotFound || pStrObj);
  return pStrObj;
}
else
{
  CrstHolder gch(&(SystemDomain::GetGlobalStringLiteralMap()
                                   ->m_HashTableCrstGlobal));
  ....
  // Make sure some other thread has not already added it.
  if (!m_StringToEntryHashTable->GetValue(&StringData, &Data))
  {
    // Insert the handle to the string into the hash table.
    m_StringToEntryHashTable->InsertValue(&StringData, (LPVOID)pEntry, FALSE);
  }
  ....
}
....

      
      



else , , String ( GetValue) , false. , else. CrstHolder gch. CrstHolder :





inline CrstHolder(CrstBase * pCrst)
    : m_pCrst(pCrst)
{
    WRAPPER_NO_CONTRACT;
    AcquireLock(pCrst);
}

      
      



AcquireLock. . AcquireLock:





DEBUG_NOINLINE static void AcquireLock(CrstBase *c)
{
  WRAPPER_NO_CONTRACT;
  ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
  c->Enter();
}

      
      



, , – Enter. , , , , : "Acquire the lock". CoreCLR . , , , , , . CrstHolder, , m_StringToEntryHashTable->InsertValue.





, else. gch , ReleaseLock:





inline ~CrstHolder()
{
  WRAPPER_NO_CONTRACT;
  ReleaseLock(m_pCrst);
}

      
      



, . , 145 ( IncrediBuild), , , 144 , . Build Monitor.





, , . " ", . , , .





.





, : Ilya Gainulin. Pitfalls in String Pool, or Another Reason to Think Twice Before Interning Instances of String Class in C#.








All Articles