web-dev-qa-db-fra.com

Ai-je atteint les limites de la taille des objets que JavaScript dans mon navigateur peut gérer?

J'incorpore un grand tableau dans les balises <script> Dans mon HTML, comme ceci (rien de surprenant):

<script>
    var largeArray = [/* lots of stuff in here */];
</script>

Dans cet exemple particulier, le tableau contient 210 000 éléments. C'est bien en dessous du maximum théorique de 231 - par 4 ordres de grandeur . Voici la partie amusante: si j'enregistre la source JS pour le tableau dans un fichier, ce fichier est> 44 mégaoctets (46 573 399 octets, pour être exact).

Si vous voulez voir par vous-même, vous pouvez télécharger depuis GitHub . (Toutes les données qui s'y trouvent sont conservées, une grande partie d'entre elles est répétée. Ce ne sera pas le cas en production.)

Maintenant, je ne suis vraiment pas préoccupé par servir autant de données. Mon serveur compresse ses réponses, donc cela ne prend vraiment pas beaucoup de temps pour obtenir les données sur le fil. Cependant, il y a une tendance vraiment désagréable pour la page, une fois chargée, à planter le navigateur. Je ne teste pas du tout en IE (il s'agit d'un outil interne). Mes cibles principales sont Chrome 8 et Firefox 3.6.

Dans Firefox, je peux voir une erreur raisonnablement utile dans la console:

Error: script stack space quota is exhausted

Dans Chrome, j'obtiens simplement la page de l'onglet triste:

enter image description here

Aller de l'avant, déjà

  • Est-ce vraiment trop de données pour nos navigateurs modernes "hautes performances"?
  • Y a-t-il quelque chose que je puisse faire * pour gérer gracieusement autant de données?

Soit dit en passant, j'ai pu faire fonctionner cela (lire: ne pas planter l'onglet) de temps en temps dans Chrome. Je pensais vraiment que Chrome, au moins, était fait de choses plus dures, mais apparemment j'avais tort ...


Modifier 1

@Crayon: Je ne cherchais pas à justifier pourquoi Je voudrais transférer autant de données dans le navigateur à la fois. Version courte: soit je résout ce problème (certes pas si facile), soit je dois résoudre toute une série d'autres problèmes. J'opte pour l'approche la plus simple pour l'instant.

@ divers: en ce moment, je ne cherche pas spécialement des moyens de réduire réellement le nombre d'éléments dans le tableau. Je sais que je pourrais implémenter la pagination Ajax ou ce que vous avez, mais cela présente son propre ensemble de problèmes pour moi à d'autres égards.

@Phrogz: chaque élément ressemble à ceci:

{dateTime:new Date(1296176400000),
 terminalId:'terminal999',
 'General___BuildVersion':'10.05a_V110119_Beta',
 'SSM___ExtId':26680,
 'MD_CDMA_NETLOADER_NO_BCAST___Valid':'false',
 'MD_CDMA_NETLOADER_NO_BCAST___PngAttempt':0}

@Will: mais j'ai un ordinateur avec un processeur à 4 cœurs, 6 gigaoctets de RAM, plus d'un demi-téraoctet d'espace disque ... et je ne demande même pas au navigateur de le faire rapidement - je demande juste pour que ça marche pas du tout !


Modifier 2

Mission accomplie!

Avec les suggestions précises de Juan ainsi que Guffa , j'ai pu faire fonctionner cela! Il semblerait que le problème était juste dans l'analyse le code source, ne fonctionnant pas réellement avec lui en mémoire.

Pour résumer le bourbier de commentaires sur la réponse de Juan: J'ai dû diviser mon grand tableau en une série de plus petits, puis Array#concat() eux, mais ce n'était pas suffisant. J'ai également dû les mettre dans des instructions var distinctes. Comme ça:

var arr0 = [...];
var arr1 = [...];
var arr2 = [...];
/* ... */
var bigArray = arr0.concat(arr1, arr2, ...);

À tous ceux qui ont contribué à résoudre ce problème: merci. Le premier tour est à moi!


* autre que l'évidence: envoyer moins de données au navigateur

63
Matt Ball

Voici ce que j'essaierais: vous avez dit qu'il s'agissait d'un fichier de 44 Mo. Cela prend sûrement plus de 44 Mo de mémoire, je suppose que cela prend beaucoup plus de 44 Mo de RAM, peut-être un demi-concert. Pourriez-vous simplement réduire les données jusqu'à ce que le navigateur ne plante pas et voir combien de mémoire le navigateur utilise?

Même les applications qui s'exécutent uniquement sur le serveur seraient bien servies pour ne pas lire un fichier de 44 Mo et le conserver en mémoire. Cela dit, je crois que le navigateur devrait être capable de le gérer, alors laissez-moi exécuter quelques tests.

(En utilisant Windows 7, 4 Go de mémoire)

Premier test J'ai coupé le tableau en deux et il n'y a eu aucun problème, utilise 80 Mo, pas de plantage

Second Test J'ai divisé le tableau en deux tableaux séparés, mais contient toujours toutes les données, utilise 160 Mo, pas de plantage

Troisième test Puisque Firefox a dit qu'il manquait de pile, le problème est probablement qu'il ne peut pas analyser le tableau à la fois. J'ai créé deux tableaux séparés, arr1, arr2 puis ai fait arr3 = arr1.concat (arr2); Il fonctionnait bien et n'utilisait que légèrement plus de mémoire, environ 165 Mo.

Quatrième test Je crée 7 de ces tableaux (22 Mo chacun) et les concatène pour tester les limites du navigateur. Le chargement de la page prend environ 10 secondes. La mémoire monte à 1,3 Go, puis redescend à 500 Mo. Donc oui chrome peut le gérer. Il ne peut tout simplement pas analyser tout à la fois car il utilise une sorte de récursivité comme le message d'erreur de la console peut le remarquer.

Answer Créez des tableaux séparés (moins de 20 Mo chacun), puis concattez-les. Chaque tableau doit être sur sa propre instruction var, au lieu de faire plusieurs déclarations avec une seule var.

Je considérerais toujours de ne chercher que la partie nécessaire, cela peut rendre le navigateur lent. cependant, si c'est une tâche interne, ça devrait aller.

Dernier point: vous n'êtes pas au niveau de mémoire maximum, juste au niveau d'analyse maximum.

82
Juan Mendes

Oui, c'est trop demander à un navigateur.

Cette quantité de données serait gérable s'il s'agissait déjà de données, mais ce ne sont pas encore des données. Considérez que le navigateur doit analyser cet énorme bloc de code source tout en vérifiant que la syntaxe s'additionne pour tout cela. Une fois analysé en code valide, le code doit s'exécuter pour produire le tableau réel.

Ainsi, toutes les données existeront dans (au moins) deux ou trois versions à la fois, chacune avec une certaine quantité de frais généraux. Comme le littéral du tableau est une instruction unique, chaque étape devra inclure toutes les données.

La division des données en plusieurs tableaux plus petits faciliterait probablement la tâche du navigateur.

13
Guffa

Avez-vous vraiment besoin de toutes les données? ne pouvez-vous pas diffuser uniquement les données actuellement nécessaires à l'aide d'AJAX? Semblable à Google Maps - vous ne pouvez pas insérer toutes les données cartographiques dans la mémoire du navigateur, elles affichent uniquement la partie que vous voyez actuellement.

N'oubliez pas que 40 Mo de données matérielles peuvent être gonflés à bien plus dans la représentation interne du navigateur. Par exemple, l'interpréteur JS peut utiliser une table de hachage pour implémenter le tableau, ce qui ajouterait une surcharge de mémoire supplémentaire. En outre, je m'attends à ce que les navigateurs stockent à la fois le code source et la mémoire JS, qui, à elle seule, double la quantité de données.

JS est conçu pour fournir une interaction UI côté client, et non pour gérer des charges de données.

ÉDITER:

Au fait, pensez-vous vraiment que les utilisateurs aimeront télécharger 40 mégaoctets de code? Il y a encore de nombreux utilisateurs avec un accès Internet à large bande. Et l'exécution du script sera suspendue jusqu'à ce que toutes les données soient téléchargées.

EDIT2:

J'ai regardé les données. Ce tableau sera certainement représenté comme une table de hachage. De nombreux articles sont également des objets, qui nécessiteront un suivi des références ... c'est-à-dire de la mémoire supplémentaire.

Je suppose que les performances seraient meilleures s'il s'agissait d'un simple vecteur de données primitives.

EDIT3: Les données pourraient certainement être simplifiées. La majeure partie d'entre elles sont des chaînes répétitives, qui pourraient être codées d'une manière ou d'une autre sous forme d'entiers ou quelque chose. De plus, mon Opera a du mal à simplement afficher le texte, et encore moins à l'interpréter.

EDIT4: Oubliez les objets DateTime! Utilisez des horodatages ou des chaînes d'ère Unix, mais pas des objets!

EDIT5: Votre processeur n'a pas d'importance car JS est monothread. Et votre RAM n'a pas d'importance non plus, la plupart des navigateurs sont 32 bits, donc ils ne peuvent pas utiliser beaucoup de cette mémoire.

EDIT6: Essayez de changer les indices du tableau en entiers séquentiels (0, 1, 2, 3 ...). Cela pourrait amener le navigateur à utiliser une structure de données de tableau plus efficace. Vous pouvez utiliser des constantes pour accéder efficacement aux éléments du tableau. Cela va réduire la taille du tableau par un énorme morceau.

5
Matěj Zábský

Essayez de récupérer les données avec Ajax en tant que page JSON. Je ne connais pas la taille exacte, mais j'ai pu extraire de grandes quantités de données dans Google Chrome de cette façon.

5
Bitsplitter

Utilisez le chargement paresseux. Ayez des pointeurs vers les données et obtenez-les lorsque l'utilisateur le demande.

Cette technique est utilisée à divers endroits pour gérer des millions d'enregistrements de données.

[Éditer]

J'ai trouvé ce que je cherchais. Défilement virtuel dans jqgrid . C'est 500k enregistrements en cours de chargement paresseux.

3
Raynos

J'essaierais de l'avoir comme une grande chaîne avec un séparateur entre chaque "élément" puis j'utiliserais split, quelque chose comme:

var largeString = "item1,item2,.......";
var largeArray = largeString.split(",");

Espérons que la chaîne n'épuise pas la pile si vite.

Edit: afin de le tester, j'ai créé un tableau factice avec 200 000 éléments simples (chaque élément un numéro) et Chrome l'a chargé en un instant. 2 000 000 d'éléments? Quelques secondes mais pas de plantage. 6 000 000 d'éléments tableau (fichier de 50 Mo) fait Chrome charge pendant environ 10 secondes mais toujours, aucun plantage dans les deux sens.

Cela m'amène donc à croire que le problème ne vient pas du tableau lui-même mais plutôt de contents .. optimisez le contenu en éléments simples puis analysez-les "à la volée" et cela devrait fonctionner.

2
Shadow Wizard