12
votes

Danger de la méthode de sous-chaîne C #?

Récemment, j'ai lu certaines des défauts avec la méthode de sous-chaîne Java - concernant spécifiquement la mémoire et comment Java conserve une référence à la chaîne d'origine. Ironiquement, je développe également une application de serveur utilisant la mise en œuvre de C # .NET de la sous-chaîne de nombreuses dizaines de fois dans une seconde. Ça m'a fait penser ...

  1. Y a-t-il des problèmes de mémoire avec le C # (.NET) string.substring ?
  2. Quelle est la performance comme sur string.substring ? Y a-t-il un moyen plus rapide de diviser une chaîne en fonction de la position de début / de fin?

6 commentaires

50 fois par minute semble guère comme une charge lourde pour moi. Des centaines à des milliers de fois par seconde seraient intenses, mais une fois chaque seconde et un peu?


@JBALL: À l'heure actuelle, c'est environ une fois par seconde, mais comme la charge du serveur augmente, l'utilisation de la sous-chaîne.


Le point n'est pas que l'utilisation du processeur serait élevée - s'il s'agit d'une application de serveur qui fonctionne pendant de nombreuses jours et qui appelle la sous-chaîne sur de longues chaînes, il pourrait toujours "fuir" une énorme mémoire à ce moment-là si .NET souffre de la même chose problème.


@Tommy, c'était votre deuxième question sur un moyen plus rapide d'obtenir une sous-chaîne que je commentais. Une fuite de mémoire est un problème qui vaut la peine de rechercher, mais une fonction de cadre principale comme .Substring devrait probablement être supposée être performante jusqu'à ce que vous voyiez des ralentissements réels que vous suivez ensuite à cette opération.


Eric Lippert a-t-il lui-même répondu à cette question:

9 Réponses :


0
votes

Je semble rappeler que les cordes de Java étaient stockées comme des personnages réels avec un début et une longueur.

Cela signifie qu'une chaîne de sous-chaîne peut partager les mêmes caractères (puisqu'ils sont immuables) et ne doivent que maintenir un début et une longueur distincts.

Donc, je ne suis pas tout à fait certain de vos problèmes de mémoire avec les chaînes Java.


concernant cet article affiché dans votre édition, il semble un peu un problème pour moi.

Sauf si vous avez l'habitude de faire des cordes énormes, prenez une petite sous-chaîne d'entre eux et laissez-les couchés, cela aura un impact presque zéro sur la mémoire.

Même si vous aviez une chaîne de 10 m et que vous avez effectué 400 sous-chaînes, vous n'utilisez que 10M pour le tableau de caractères sous-jacent - il ne fait pas 400 copies de cette sous-chaîne. Le seul impact de la mémoire est le bit de départ / long de chaque objet de sous-chaîne.

L'auteur semble se plaindre qu'ils lisent une ficelle énorme en mémoire que je ne voulais qu'un peu, mais tout cela a été gardé - ma suggestion serait qu'ils voudront peut-être repenser la manière dont ils traitent leurs données :-)

Appeler cela, un bug Java est un énorme étirement aussi. Un bug est quelque chose qui ne fonctionne pas à la spécification. C'était une décision de conception pour améliorer les performances, à court de mémoire, car vous ne comprenez pas comment les choses fonctionnent ne sont pas un bug, Imnsho. Et c'est définitivement pas une fuite de mémoire.


Il y avait une bonne suggestion la bonne suggestion dans les commentaires à cet article, que le GC pourrait récupérer plus agressivement des morceaux de chaînes inutilisées en les comprimant.

Ceci est pas quelque chose que vous voudriez faire lors d'un premier passage GC, car il serait relativement cher. Cependant, lorsque toutes les autres opérations de GC n'avaient pas réussi à récupérer suffisamment d'espace, vous pourriez le faire.

Malheureusement, il signifierait presque certainement que le tableau sous-jacent Char devait conserver une enregistrement de tous les objets de chaîne qui l'a référencée, de sorte qu'il pourrait à la fois de comprendre ce que les bits étaient inutilisés et Modifiez tous les champs de démarrage et de longueur d'objet String.

Ceci en soi peut introduire des impacts de performance inacceptables et, en plus de cela, si votre mémoire est si courte pour que cela soit un problème, vous ne pouvez même pas être en mesure d'allouer suffisamment d'espace pour une version plus petite de la chaîne. < / p>

Je pense que si la mémoire s'épuise, je préférerais probablement pas pour maintenir ce cartographie de charrette à chaîne pour rendre ce niveau de c gc possible, mais je préférerais cela mémoire à utiliser pour mes cordes.


Comme il y a une solution de contournement parfaitement acceptable et que de bons codeurs devraient savoir sur les faiblesses de leur langue de choix, je soupçonne que l'auteur a raison - il ne sera pas corrigé.

Pas parce que les développeurs Java sont trop paresseux, mais parce que ce n'est pas un problème.

Vous êtes libre d'implémenter vos les méthodes de chaîne qui correspondent aux cméres C # (qui ne partagent pas les données sous-jacentes, sauf dans certains scénarios limités). Cela réparera vos problèmes de mémoire, mais au prix d'une performance touchée, car vous devez copier les données chaque fois que vous appelez la sous-chaîne. Comme pour la plupart des choses en elle (et la vie), c'est un compromis.


1 commentaires

Je dois être en désaccord avec la déclaration "à court de mémoire parce que vous ne savez pas comment les choses fonctionnent n'est pas un bogue" . La documentation pour la sous-chaîne des États: renvoie une nouvelle chaîne qui est une sous-chaîne de cette chaîne . Il ne donne aucun indice que la chaîne renvoyée est épingler la chaîne d'origine en mémoire . Les documents doivent donc clairement indiquer clairement le comportement réel ou cette "optimisation" doit être repoussé. C'est votre choix - que ce soit, soit les documents sont défectueux ou la mise en œuvre est. Les développeurs ne devraient pas avoir à examiner la mise en œuvre interne de telles méthodes comprennent comment les utiliser correctement.



1
votes

Il est toujours bon d'essayer et mesurez les millisecondes écoulés. xxx


2 commentaires

Merci! Ceci est très utile!


utilisait des fourmis de profileur pour les problèmes liés à la GC. Vous vous demandez une meilleure option?




3
votes

Chaque fois que vous utilisez la sous-chaîne, vous créez une nouvelle instance de chaîne - elle doit copier le caractère de l'ancienne chaîne au nouveau, ainsi que la nouvelle allocation de mémoire associée - et n'oubliez pas que ce sont des caractères Unicode. Cela peut ou non être une mauvaise chose - à un moment donné, vous voulez utiliser ces personnages quelque part quand même. Selon ce que vous faites, vous voudrez peut-être que votre propre méthode ne trouve que les index dans la chaîne que vous pouvez ensuite utiliser ultérieurement.


0 commentaires

0
votes

Le CLR (par conséquent c #'s) implémentation de SUBSTRIND ne conserve pas une référence à la chaîne source, de sorte qu'il ne dispose pas du problème de "fuite de mémoire" des chaînes Java.


0 commentaires

0
votes

La plupart de ces types de problèmes de chaîne sont parce que la chaîne est immuable. La classe StringBuilder est destinée au moment où vous faites beaucoup de manipulations de chaîne:

http://msdn.microsoft.com/fr -Us / Bibliothèque / 2839D5H5 (vs.71) .aspx

Notez que le problème réel est l'allocation de la mémoire plutôt que sur la CPU, bien que la mémoire excessive est une mémoire excessive de la mémoire cupraph ...


0 commentaires

1
votes

Dans le cas de la fuite de mémoire Java, on peut expérimenter lors de l'utilisation de la sous-chaîne, il est facilement corrigé en instanciant d'un nouvel objet à chaîne avec le constructeur de copie (qui correspond à un appel du formulaire "nouvelle chaîne (chaîne)"). En utilisant que vous pouvez supprimer toutes les références à l'original (et dans le cas où il s'agit en réalité d'une chaîne de problème, plutôt grande) et n'entre entretenez que les parties dont vous avez besoin en mémoire.

Non Idéal, en théorie, la JVM pourrait être plus intelligente et compresser l'objet String (comme on l'a suggéré ci-dessus), mais cela obtient le travail avec ce que nous avons maintenant.

comme pour C #, comme cela a été dit, ce problème n'existe pas.


0 commentaires

1
votes

juste pour ajouter une autre perspective à ce sujet.

Hors de la mémoire (la plupart des temps) ne signifie pas que vous avez utilisé toute la mémoire. Cela signifie que votre mémoire a été fragmentée et la prochaine fois que vous souhaitez affecter un morceau, le système est incapable de trouver un morceau de mémoire contigu pour répondre à vos besoins.

Allocations fréquentes / Deallocations provoquera une fragmentation de la mémoire. Le GC peut ne pas être en mesure de dégraisser dans le temps Sue aux types d'opérations que vous faites. Je sais que le serveur GC in .NET est plutôt bon à propos de la mémoire de fragmentation, mais vous pouvez toujours affamer (empêcher la GC de faire une collecte) le système en écrivant un mauvais code.


1 commentaires

Je ne vois aucune façon de pouvoir empêcher le GC de collecter. La collecte se produit lorsque le GC voit qu'il y a trop de "pression de la mémoire", mais IIRC Cette vérification est effectuée lorsqu'une allocation se produit, de sorte que vous ne pouvez donc pas allouer la mémoire sans donner à la GC la possibilité de collecter (IIRC, tous les threads seront suspendus. tandis que la collection se produit).



0
votes

Pour la mémoire de profilage Lorsque vous développez, vous pouvez utiliser ce code: xxx

à propos de paramètre ForceFullCollection : "Si le paramètre ForceFullCollection est vrai, cette méthode attend un court intervalle Avant de retourner pendant que le système recueille des ordures et finalise des objets. La durée de l'intervalle est une limite spécifiée à l'intérieur déterminée par le nombre de cycles de collecte des ordures terminés et la variation de la quantité de mémoire récupérée entre les cycles. Le collecteur des ordures ne garantit pas que tout la mémoire inaccessible est collectée. " Méthode GC.GETTOTALMORIE

Bonne chance!;)


0 commentaires