36 votes

Est-il efficace de la mémoire de remplacement de java.lang.Chaîne?

Après la lecture de cet ancien article de mesure de la consommation de mémoire de plusieurs types d'objet, j'ai été étonné de voir comment beaucoup de mémoire Strings utiliser en Java:

length: 0, {class java.lang.String} size = 40 bytes
length: 7, {class java.lang.String} size = 56 bytes

Bien que l'article a quelques conseils pour réduire cet effet, je ne trouve pas entièrement satisfaisant. Il semble être un gaspillage d'utiliser char[] pour le stockage des données. L'amélioration évidente pour la plupart des langues occidentales serait d'utiliser byte[] et un encodage comme de l'UTF-8 au lieu de cela, car vous n'avez qu'un seul octet pour stocker la plus fréquente des caractères, puis au lieu de deux octets.

Bien sûr, on pourrait utiliser String.getBytes("UTF-8") et new String(bytes, "UTF-8"). Même la surcharge de la Chaîne de l'instance elle-même aurait disparu. Mais alors là, vous perdez très pratique, les méthodes comme equals(), hashCode(), length(), ...

Soleil a un brevet sur byte[] représentation des Chaînes de caractères, dans la mesure où je peux dire.

Cadres pour une représentation efficace de la chaîne d'objets dans des environnements de programmation Java
... Les techniques peuvent être mises en place pour créer Java chaîne objets comme des tableaux de caractères d'un octet quand il est approprié ...

Mais je n'ai pas réussi à trouver une API pour le brevet.

Pourquoi dois-je prendre soin?
Dans la plupart des cas je n'en ai pas. Mais j'ai travaillé sur des applications avec d'énormes caches, contenant beaucoup de Cordes, qui aurait bénéficié de l'aide de la mémoire de manière plus efficace.

Quelqu'un sait d'une telle API? Ou est-il une autre façon de garder votre empreinte mémoire pour les Chaînes petit, même au prix de la performance du CPU ou de la plus laide de l'API?

Merci de ne pas répéter les suggestions de l'article ci-dessus:

  • variante de String.intern() (éventuellement avec SoftReferences)
  • le stockage d'un seul char[] et l'exploitation de l'actuel String.subString(.) mise en œuvre pour éviter la copie des données (méchant)

Mise à jour

J'ai couru le code de l'article sur actuel de Sun JVM (1.6.0_10). Il a donné les mêmes résultats qu'en 2002.

24voto

haylem Points 11504

Avec un Peu d'Aide de la JVM...

AVERTISSEMENT: Cette solution est aujourd'hui obsolète dans les nouvelles versions de Java SE. Voir d'autres ad-hoc, des solutions plus ci-dessous.

Si vous utilisez un HotSpot de la JVM, depuis Java 6 update 21, vous pouvez utiliser cette option de ligne de commande:

-XX:+UseCompressedStrings

Les Options JVM page se lit comme suit:

Utiliser un byte[] pour les Chaînes de caractères qui peut être représenté comme pur ASCII. (Présenté dans Java 6 Update 21 la Libération des Performances)

Mise à JOUR: Cette fonctionnalité a été cassé dans une version ultérieure et doit être fixé à nouveau en Java SE 6u25, comme mentionné par l' 6u25 b03 notes de version (mais nous ne le voyez pas dans la 6u25 sortie de la version finale des notes). Le rapport de bug 7016213 n'est pas visible pour des raisons de sécurité. Donc, à utiliser avec précaution et vérifiez d'abord. Comme n'importe quel -XX option, il est considéré comme expérimental et sous réserve de modification sans préavis, donc c'est probablement pas toujours préférable de ne pas les utiliser que dans le sac de démarrage d'un serveur de production.

Mise à JOUR 2013-03 (grâce à un commentaire par Aleksey Maximus): Voir cette question relative à la et son a accepté de répondre. L'option semble être maintenant décédé. Ceci est confirmé dans le bug 7129417 rapport.

La Fin Justifie les Moyens

Avertissement: (Laid) des Solutions pour des Besoins Spécifiques

C'est un peu hors de la boîte et de niveau inférieur, mais puisque vous le demandez... ne frappez pas sur le messager!

Votre Propre Plus Léger Représentation De Chaîne

Si ASCII est très bien pour vous, alors pourquoi ne pas vous juste dans le déploiement de votre propre mise en œuvre?

Comme vous l'avez mentionné, vous pourriez byte[] au lieu de char[] en interne. Mais ce n'est pas tout.

Pour faire encore plus léger, au lieu d'emballer vos tableaux d'octets dans une classe, pourquoi ne pas simplement utiliser une classe d'aide contenant essentiellement des méthodes statiques d'exploitation sur ces tableaux d'octets que vous passer? Bien sûr, il va se sentir assez C-ish, mais il pourrait fonctionner, et serait vous faire économiser de l' énorme surcharge qui va avec, String objets.

Et bien sûr, il manquerait quelques belles fonctionnalités... à moins que votre re-mettre en œuvre. Si vous avez vraiment besoin d'eux, alors il n'y a pas beaucoup de choix. Grâce à OpenJDK et beaucoup d'autres bons projets, vous pourriez très bien dans le déploiement de votre propre fugly LiteStrings de la classe qui vient de fonctionner sur byte[] paramètres. Vous aurez envie de prendre une douche à chaque fois que vous avez besoin pour appeler une fonction, mais vous aurez sauvé des tas de mémoire.

Je vous recommande de la faire ressembler de près l' String classe du contrat et de fournir des cartes et des constructeurs pour convertir de et vers String, et vous pouvez aussi avoir des adaptateurs vers et à partir d' StringBuffer et StringBuilder, ainsi que certaines miroir implémentations d'autres choses que vous pourriez avoir besoin. Certainement un morceau de travail, mais pourrait être mieux (voir un peu en dessous de la "Faites que ça compte!" de la section).

Sur la Volée de Compression/Décompression

Vous pourriez très bien compresser vos chaînes en mémoire et de les décompresser à la volée lorsque vous en avez besoin. Après tout, vous avez seulement besoin d'être en mesure de les lire lorsque vous y accédez, à droite?

Bien sûr, étant que les violents signifie:

  • plus complexe (donc moins facile à gérer) du code,
  • plus de puissance de traitement,
  • relativement longues chaînes sont nécessaires pour le type de compression (ou de compact de plusieurs chaînes en un seul par la mise en œuvre de votre propre système magasin, à la compression plus efficace).

Faire Les Deux

Pour un mal de tête, bien sûr, vous pouvez faire tout cela:

  • C-ish de la classe helper,
  • tableaux d'octets,
  • à la volée comprimé magasin.

Assurez-vous que l'open source. :)

Faites que ça compte!

Par le chemin, voir ce grand exposé sur la Construction Efficace de la Mémoire des Applications Java par N. Mitchell et G. Sevitsky: [version 2008], [version 2009].

À partir de cette présentation, nous voyons que l' 8-chaîne de char mange 64 octets sur un système 32 bits (96 pour un système 64 bits!!), et la plupart de cela est dû à la JVM de frais généraux. Et à partir de cet article , nous voyons que l' 8-tableau d'octets de manger "que" 24 octets: 12 octets d'en-tête, 8 x 1 octet + 4 octets de l'alignement).

Semble que cela pourrait être utile si vous avez vraiment manipuler un grand nombre de ce genre de choses (et éventuellement d'accélérer un peu les choses, comme vous voulez passer moins de temps à allouer de la mémoire, mais ne pas me citer sur ce point de repère et il; et il dépendra grandement sur votre mise en œuvre).

21voto

Alex Miller Points 28225

En terre Cuite, nous avons quelques cas où nous compresser de grandes Chaînes comme ils sont envoyés à travers le réseau et en fait de les laisser comprimé jusqu'à ce que la décompression est nécessaire. Nous faisons cela en convertissant le char[] pour byte[], la compression de l'byte[], puis codage byte[] de retour dans l'original char[]. Pour certaines opérations comme le hachage et la durée, nous pouvons répondre à ces questions sans les décoder le comprimé de la chaîne. Pour les données, comme des grosses chaînes XML, vous pouvez obtenir substantielle de la compression de cette façon.

Déplacement de la compression de données à travers le réseau est une victoire définitive. Garder comprimé dépend du cas d'utilisation. Bien sûr, nous avons quelques boutons pour désactiver cette option et modifier la longueur à laquelle la compression s'allume, etc.

Tout cela est fait avec l'octet code d'instrumentation sur java.lang.La chaîne que nous avons trouvée est très délicate en raison de la précocité de la Chaîne est utilisée dans de démarrage mais est stable si vous suivez quelques règles.

10voto

Stephen Denne Points 17031

L'article souligne deux choses:

  1. Des tableaux de caractères augmentation en blocs de 8 octets.
  2. Il y a une grande différence de taille entre char[] et des objets String.

La surcharge est due notamment un char[] référence de l'objet, et de trois entiers: un décalage, une longueur, et de l'espace pour le stockage de la Chaîne de hashcode, en plus de la norme de la surcharge d'être simplement un objet.

Légèrement différente de la Chaîne.stagiaire(), ou un tableau de caractères utilisé par la Chaîne de caractères.substring() est à l'aide d'un seul char[] pour toutes les Chaînes, ce qui signifie que vous n'avez pas besoin de stocker l'objet de référence dans votre enveloppe Chaîne comme objet. Vous auriez encore besoin de l'offset, et de vous présenter un (grand) nombre maximal de caractères que vous pouvez avoir au total.

Vous n'avez plus besoin de la longueur si vous utilisez une fin spéciale de la chaîne de marqueur. Qui enregistre quatre octets pour la longueur, mais vous coûte deux octets pour le marqueur, ainsi que le temps supplémentaire, la complexité et la saturation de la mémoire tampon des risques.

L'espace-temps d'échanges, de ne pas stocker la valeur de hachage peut vous aider si vous n'en avez pas besoin souvent.

Pour une application que j'ai travaillé, l'endroit où je devais super rapide et efficace en terme de mémoire traitement d'un grand nombre de chaînes, j'ai été capable de conserver les données dans sa forme codée, et de travailler avec des tableaux d'octets. Mon encodage de sortie a été le même que celui de mon encodage en entrée, et je n'ai pas besoin de décoder des octets pour les caractères, ni coder retour à octets de nouveau de sortie.

En outre, j'ai pu quitter l'entrée de données dans le tableau d'octets, il a été à l'origine de lire un fichier mappé en mémoire.

Mes objets se composait d'un int offset (la limite adapté à ma situation), un int longueur, et un int hashcode.

java.lang.La chaîne a été le familier marteau pour ce que je voulais faire, mais pas le meilleur outil pour le travail.

7voto

matt b Points 73770

Je pense que vous devriez être très prudent sur les fondements des idées et/ou des hypothèses hors d'un javaworld.com l'article à partir de 2002. Il y a eu beaucoup, beaucoup de changements pour le compilateur et de la JVM dans les six ans depuis. À tout le moins, de tester votre hypothèse et de la solution à l'encontre d'un moderne JVM d'abord pour s'assurer que la solution est encore en vaut la chandelle.

7voto

benjismith Points 8739

Un interne de l'encodage UTF-8 a ses avantages (comme la plus petite empreinte mémoire que vous l'avez fait remarquer), mais il a des inconvénients aussi.

Par exemple, la détermination de la nature de longueur (plutôt que de l'octet de longueur) d'une codé en UTF-8 string est un O(n) opérations. Dans une java string, le coût de la détermination de la nature de longueur O(1), tout en générant de l'UTF-8 représentation est O(n).

Il est tout au sujet des priorités.

De données-conception de la structure peut souvent être considérée comme un compromis entre la vitesse et de l'espace. Dans ce cas, je pense que les concepteurs de Java chaîne d'API fait un choix sur la base de ces critères:

  • La classe String doit prendre en charge tous les possibles de caractères unicode.

  • Bien que unicode définit 1 octet 2 octets 4 octets variantes, le 4 octets personnages sont (dans la pratique) assez rare, il est donc normal de les représenter comme des paires de substitution. C'est pourquoi java utilise un 2-byte char primitive.

  • Quand les gens appellent length(), indexOf(), et charAt() les méthodes, ils s'intéressent à la position de caractère, pas la position d'octet. Afin de créer rapidement des implémentations de ces méthodes, il est nécessaire d'éviter l'interne de l'encodage UTF-8.

  • Les langages tels que C++ de faire le programmeur de la vie plus compliquée par la définition des trois différents types de caractères et de forcer le programmeur à choisir entre eux. La plupart des programmeurs commencer à l'aide de simples chaînes ASCII, mais quand ils ont besoin de support des caractères internationaux, le processus de modification du code pour utiliser des caractères multioctets est extrêmement douloureux. Je pense que le Java concepteurs fait un excellent compromis choix en disant que toutes les chaînes sont constitués de caractères de 2 octets.

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