27 votes

Pourquoi string.Substring ne partage-t-il pas la mémoire avec la chaîne source?

Comme nous le savons tous, des chaînes dans le .NET sont immuables. (Eh bien, pas à 100% totalement immuable, mais immuable par la conception et utilisée comme telle par toute personne raisonnable, de toute façon.)

De ce fait, il fondamentalement OK que, par exemple, le code suivant juste stocke une référence à la même chaîne à deux variables:

string x = "shark";
string y = x.Substring(0);

// Proof:
fixed (char* c = y)
{
    c[4] = 'p';
}

Console.WriteLine(x);
Console.WriteLine(y);

La au-dessus de sorties:

sharp
sharp

Clairement x et y se réfèrent à la même string objet. Alors, voici ma question: pourquoi n'ai - Substring toujours partager l'état avec la chaîne source? Une chaîne est essentiellement un char* pointeur avec une longueur de, droite? Il me semble donc les éléments suivants doivent au moins en théorie, être autorisé à assigner un seul bloc de mémoire pour contenir 5 caractères, avec deux variables pointant simplement à différents endroits à l'intérieur qu' (immuable) bloc:

string x = "shark";
string y = x.Substring(1);

// Does c[0] point to the same location as x[1]?
fixed (char* c = y)
{
    c[0] = 'p';
}

// Apparently not...
Console.WriteLine(x);
Console.WriteLine(y);

La au-dessus de sorties:

shark
park

19voto

Guffa Points 308133

Pour deux raisons:

  • La chaîne de méta-données (par ex. longueur) est stocké dans le même bloc de mémoire que les personnages, afin de permettre à la chaîne d'utiliser une partie des données de caractère de l'autre de la chaîne signifierait que vous auriez à allouer deux blocs de mémoire pour la plupart des chaînes de caractères au lieu d'un. Comme la plupart des chaînes ne sont pas des sous-chaînes de cordes, ce supplément d'allocation de mémoire serait plus la consommation de mémoire que ce que vous pourriez gagner en réutilisant une partie des chaînes de caractères.

  • Il y a un extra NUL caractère stocké après le dernier caractère de la chaîne, pour que la chaîne a également utilisable par le système des fonctions qui s'attendent à une chaîne terminée par null. Vous ne pouvez pas mettre que des extra NUL personnage après une sous-chaîne qui fait partie d'une autre chaîne.

9voto

Joe Points 60749

Je crois que les chaînes C # sont terminées par null - bien que ce soit un détail d'implémentation qui ne devrait pas concerner les consommateurs gérés, il y a des cas (par exemple marshaling) où c'est important.

De plus, si une sous-chaîne partage un tampon avec une chaîne beaucoup plus longue, cela signifie qu'une référence à la sous-chaîne courte empêcherait la collecte de la chaîne plus longue. Et la possibilité d'un nid de rats de références de chaîne qui se réfèrent au même tampon.

4voto

sleske Points 29978

Pour ajouter d'autres réponses:

Apparemment, la norme Java classes ce faire: La chaîne de caractères retournée par String.substring() réutilise l'intérieur tableau de caractères de la chaîne d'origine (la source, ou de regarder les sources JDK de Sun).

Le problème est que cela signifie que la Chaîne d'origine ne peut pas être GCed jusqu'à ce que toutes les sous-chaînes sont admissibles pour le GC (aussi bien qu'ils partagent le support de tableau de caractères). Cela peut entraîner des pertes de mémoire si vous commencez avec une grande chaîne, et d'en extraire certaines petites chaînes hors de lui, avant de le jeter sur la grosse corde. Qui serait commun lors de l'analyse d'un fichier d'entrée, par exemple.

Bien sûr, un savant GC peut contourner ce problème en copiant le tableau de caractères lorsque cela en vaut la peine (la JVM de Sun peut le faire, je ne sais pas), mais la complexité supplémentaire pourrait être une raison de ne pas mettre en œuvre ce partage de comportement à tous.

1voto

supercat Points 25534

Il y a un certain nombre de façons quelque chose comme Chaîne de caractères pourraient être mises en œuvre:

  1. Avoir une "Chaîne" de l'objet effectivement contenir un tableau, avec l'implication que tous les caractères dans le tableau sont dans la chaîne. C'est ce qu' .net ne fait.
  2. Ont tous "Chaîne" être une classe qui contient un tableau de référence avec un décalage de départ et la longueur. Problème: la Création de la plupart des chaînes de nécessiterait l'instanciation de deux objets au lieu d'une.
  3. Ont tous "Chaîne" être une structure qui contient un tableau de référence avec un décalage de départ et la longueur. Problème: les cessions à des champs de type chaîne ne serait plus atomique.
  4. Ont deux ou plus de deux types de "Chaîne" des objets, ceux qui contiennent tous les caractères dans un tableau, et ceux qui contiennent une référence à une autre chaîne avec un décalage et de la longueur. Problème: Cela nécessiterait de nombreuses méthodes de chaîne virtuelle.
  5. Ont tous "Chaîne" être une classe spéciale qui comprend un décalage de départ et la longueur, une référence d'objet à ce qui peut ou peut ne pas être le même objet, et intégré dans le tableau de caractères. Ce serait gaspiller un peu d'espace dans le cas courant où une chaîne de caractères contient ses propres personnages (parce que tous d'entre eux), mais permettrait le même code pour travailler avec des chaînes de caractères qui contiennent leurs propres caractères ou des chaînes "emprunter" des autres.
  6. Ont un usage général ImmutableArray<T> type (qui héritent de ReadableArray<T>), et ont un ImmutableArray<Char> être interchangeables avec de la Ficelle. Il existe de nombreuses utilisations pour immuable tableaux; String est probablement l'utilisation la plus courante de cas, mais pas le seul.
  7. Ont un usage général ImmutableArray de type<T> type que ci-dessus, mais également une ImmutableArraySegment<T> classe, héritant de ImmutableArrayBase<T>. Cela nécessiterait beaucoup de méthodes pour être virtuel, et serait probablement mon préféré possibilité.

Notez que la plupart de ces approches présentent des limitations importantes dans au moins certains des scénarios d'utilisation.

0voto

vityanya Points 440

après avoir examiné la méthode Substring avec réflecteur, j'ai compris que si vous passez 0 dans la méthode substriong - cela retournera le même objet.

 [SecurityCritical]
private unsafe string InternalSubString(int startIndex, int length, bool fAlwaysCopy)
{
    if (((startIndex == 0) && (length == this.Length)) && !fAlwaysCopy)
    {
        return this;
    }
    string str = FastAllocateString(length);
    fixed (char* chRef = &str.m_firstChar)
    {
        fixed (char* chRef2 = &this.m_firstChar)
        {
            wstrcpy(chRef, chRef2 + startIndex, length);
        }
    }
    return str;
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X