4
votes

Le moyen le plus rapide de libérer la mémoire de stringbuilder

Quel est le moyen le plus rapide de libérer la mémoire de stringbuilder.

 sb.delete(0,sb.length());
 sb = null;

Voici les extraits de code que j'ai essayé de libérer de la mémoire le plus rapidement possible et de rendre l'objet éligible pour le ramasse-miettes p>

Option 1:

 sb.delete(0,sb.length());

Selon ce que je comprends, toutes les données du constructeur de chaînes seront supprimées et l'objet sera éligible pour le ramasse-miettes dès qu'il sera expulsé in, mais la mémoire du texte qui est occupée par stringbuilder sera libérée. La valeur de la chaîne sera-t-elle également supprimée du tas ou stockée dans le pool de chaînes?

Option 2:

 sb.setLength(0);

Cela réinitialisera la longueur du constructeur de chaînes mais il ne sera pas récupéré.

Option 3:

sb = null;

Cela réinitialisera le constructeur de chaînes en supprimant toutes les données qu'il contient mais ne sera pas récupéré.

Option 4:

 StringBUilder sb = new StringBuilder();
 sb.apppend(maximum value possible);

Cela réinitialisera le générateur de chaînes et le rendra également éligible pour le ramasse-miettes?!


3 commentaires

Vous ne pouvez pas garantir que la mémoire sera libérée à un moment donné, voire pas du tout. Tout dépend du garbage collector et dépend de l'implémentation et de la configuration de la JVM.


si la libération de mémoire est votre première préoccupation, vous pouvez créer du code natif, le lier à JNI et gérer vous-même la mémoire, mais en le faisant, vous échouerez en grande partie à l'utilisation de Java. Comme GhostCat l'a demandé, pourquoi?


Re string pool: Le contenu des StringBuilder s ne va jamais là-bas. Jamais, jamais . Seuls les String réels y vont, et ensuite seulement si vous les y mettez spécifiquement au moment de l'exécution en appelant .intern () , ou si le compilateur les a mis là au moment de la compilation.


5 Réponses :


0
votes

Une solution possible comprend 3 étapes:

  1. appelez sb.setLength (0) . Cela définira la longueur interne du générateur de chaînes sur 0 et est nécessaire pour l'étape 2.
  2. appelez sb.trimToSize () . Cela remplacera le tableau value par un nouveau tableau découpé, dans notre cas un char [] de taille 0
  3. sb = null supprimera votre référence au StringBuilder .

Si le générateur de chaînes n'est utilisé que par vous et n'est passé nulle part ailleurs, cela devrait créer le tableau value précédent et le générateur de chaînes eglibe pour le ramasse-miettes.

Cependant, le moment où cela se produit dépend toujours de la JVM et donc le "moyen le plus rapide" n'est probablement pas possible car il est en fait assez aléatoire.


8 commentaires

Vous pouvez également appeler System.gc (); qui demandera à la JVM de donner la priorité à un garbage collection ... Mais il n'est pas garanti de fonctionner ... ce n'est pas nécessaire. Et s'il vous plaît ne le faites pas, pour la santé mentale collective de vos collaborateurs.


Les deux premières étapes libèrent-elles la mémoire ou seront-elles libérées après le garbage collection?


@AhmadQureshi vous supprimez la référence à la valeur -array dans StringBuilder mais elle reste en mémoire jusqu'à ce que GC la récupère. Nous disons simplement à la JVM que nous n'en avons plus besoin


Ils devraient le libérer après un garbage collection. Je pense que la seule façon de le faire plus tôt serait la méthode sun.misc.Unsafe - freeMemory . Ce n'est vraiment pas une bonne idée cependant ... Considérez pourquoi vous pensez avoir besoin de libérer la mémoire rapidement.


@Lino "appelle sb.setLength (0). Cela remplira le tableau de valeurs à l'intérieur du générateur de chaînes avec \ 0 (le" null "-char)" - cela ne se produit que lorsque vous appelez setLength avec un argument plus grand que la longueur actuelle . SetLength (0) n'aura aucun effet par rapport à ce que l'OP est après.


@Lino votre édition ne reflétait pas ce que je voulais dire. Même si votre chaîne actuelle comporte 20 caractères, lorsque vous appelez setLength (0) , elle ne fera toujours rien à part définir count sur 0. Il y a inutile de définir les caractères inutilisés sur '\ 0' ; cela ne ferait que perdre du temps CPU. Il ne définit les caractères à 0 que lorsque vous développez la chaîne pour la rendre plus grande qu'elle ne l'est actuellement.


@KlitosKyriacou vous avez raison, encore une fois, j'ai corrigé ma réponse, la longueur définie est juste utilisée pour la deuxième étape car trimToSize () remplacera alors le tableau d'origine par un vide


Faire les étapes 1 et 2 lorsque vous allez faire l'étape 3 n'effacera probablement pas la mémoire plus rapidement (et ajoute une surcharge inutile à votre programme). Il s'agit simplement de cargoculting.



2
votes

Le seul moyen sûr de s'assurer que le StringBuilder est éligible pour le garbage collection est le même que tous les objets: supprimez toutes les références à celui-ci. Cela signifie le définir explicitement sur null ou le laisser tomber hors de portée.

Au-delà de cela, vous n'avez aucun contrôle sur le moment où la mémoire est libérée. Tout ce que vous pouvez faire est de demander le ramasse-miettes, et c'est rarement une bonne idée (sans oublier que vous demandez simplement, et non que vous ordonnez à la JVM de ramasser des ordures).

Les autres choix que vous avez proposés ne sont pas garantis pour libérer toute la mémoire à moins que vous n'ayez fait une plongée approfondie dans le code et que vous soyez sûr que vous videz complètement l'état de l'objet. C'est pourquoi il est généralement préférable d'annuler la référence entière de l'objet et de recommencer si vous voulez vous assurer que tout est "propre".


0 commentaires

3
votes

Simple: allez avec

sb = null;

et oubliez le reste.

Pourquoi? Dès que le GC peut décider qu'un objet est éligible pour le garbage collection, il sait également que toutes les choses référencées à partir de cet objet sont éligibles. Et étant donné que vous n’avez aucun contrôle sur le démarrage du GC, le nettoyage des objets et la libération de la mémoire, l’affectation ci-dessus est vraiment suffisante.

Comme elle communique clairement votre intention , puis aide le GC à faire son travail. En fait, un code bien écrit n'aurait probablement même pas besoin de cette déclaration.

Maintenant, ce n'est peut-être pas assez bon. Mais alors on parlerait d'un vrai problème de performance, dans un contexte très défini et étroit. Dans ce cas, vous ne vous fiez pas aux ouï-dire, vous commencez à mesurer.

En d'autres termes: si vous n'avez pas de réel problème de performances qui se manifeste dans votre configuration, et qui nécessite une action, alors vous optez pour un code simple et clair, évitant toutes sortes d'optimisations "au niveau du code source". Mais si vous rencontrez un réel problème de performances, traitez-le en conséquence: en identifiant la cause profonde, et probablement en mesurant comment les différentes options fonctionnent dans la réalité.

Tout le reste sent bon. d'optimisation prématurée!


0 commentaires

4
votes

Si vous voulez vraiment que la mémoire soit libérée le plus rapidement possible, alors les options GC sont là où vous devez regarder, pas le code.

Une analyse détaillée des options de GC à cette fin va au-delà de ce qui peut raisonnablement convenir ici - mais spécifiquement, le nouveau Shenandoah GC peut être réglé pour être beaucoup plus agressif pour libérer de la mémoire que G1 ou marque simultanée / sweep, c'est donc probablement ce que vous voudriez utiliser. Vous pouvez le spécifier avec -XX: + UseShenandoahGC . (C'est spécifique à OpenJDK et expérimental à ce stade, mais si vous voulez le comportement que vous recherchez, c'est celui qu'il vous faut.)

Pour des temps de publication rapides, vous voudrez utiliser une petite valeur pour ShenandoahUncommitDelay et ShenandoahGuaranteedGCInterval . Des valeurs plus petites ici (en gros) exécuteront le GC plus souvent et plus agressivement, donc en utilisant plus de cycles CPU - mais cela aura l'effet que vous recherchez, cette mémoire sera libérée incroyablement rapidement par rapport aux incarnations précédentes du GC. < / p>

Si vous voulez simplement vous assurer que la mémoire est libérée afin qu'elle soit éligible pour le ramasse-miettes rapidement, il vous suffit de vous assurer que toutes les références à ce StringBuilder sont mis à null à la première occasion. Même dans ce cas, je vous encourage à profiler et à vérifier si vous devez le faire explicitement - dans de nombreux cas maintenant, la JVM est suffisamment intelligente pour marquer les objets comme éligibles pour GC même s'ils sont techniquement toujours dans la portée ( tant qu'il peut voir qu'ils ne sont plus référencés pour le reste de cette portée.)


3 commentaires

@GhostCat Oui, OpenJDK. J'aurais dû noter que ce n'est pas standard (et expérimental) dans la réponse.


On y va ... ;-)


@MichaelBerry excellente réponse! notez simplement que toutes les versions de jdk n'ont pas Shenandoah (vous ne pouvez même pas les activer) et ShenandoahUncommitDelay ne libère que les régions qui étaient vides pendant cette période.



2
votes

Laissez simplement l'objet StringBuilder sortir de la portée (à l'accolade fermante) et n'encombrez pas votre code avec des instructions inutiles dans une vaine tentative pour que le garbage collector fasse son travail plus rapidement. Lorsque l'objet StringBuilder sort de la portée, il devient éligible pour le garbage collection. Gardez votre code source propre.


2 commentaires

Droite! Même moi, je voulais être comme ça mais les seniors comprennent à peine :)


On dirait que vos aînés viennent d'un langage de programmation différent avec ses propres paradigmes. Je leur expliquerais gentiment que ce n'est vraiment pas ainsi que Java fonctionne.