2012-06-25 15 views
28

Próbuję zbudować stronę internetową, do której potrzebuję przeskoczyć około 100MB danych w JavaScript. W różnych przeglądarkach napotykam błędy "przekroczenia maksymalnego limitu stosu połączeń" przy różnych ilościach danych.Stos vs. Stert w JavaScript? (Przekroczono maksymalny rozmiar stosu wywołań)

Czy mogę rozwiązać ten problem, przechodząc przez mój kod i próbując przenieść zmienne lokalne wewnątrz funkcji do bardziej globalnego zakresu, aby spróbować je przypisać do sterty zamiast stosu? A może te pojęcia nie istnieją w JavaScript? (O ile wiem, nie mam żadnych dużych pętli rekursywnych w moich danych, więc to naprawdę kilka ogromnych łańcuchów/tablic numerycznych, które wydają się być przyczyną błędu)

Jeśli nie jest to możliwe , czy są sposoby, aby poprosić przeglądarkę o zarezerwowanie większej ilości pamięci?

+3

+1 dla interesującego posta – pixelbobby

+4

Nie rozumiał, co widzisz . To, co się stało, to funkcja rekursywna, czyli funkcja, która sama wywołuje (lub wywołuje inną funkcję, która wywołuje pierwszą) prawdopodobnie przez przypadek. – Ben

+0

(powiązane) http://stackoverflow.com/questions/6602864/stack-and-heap-in-v8-javascript –

Odpowiedz

16

Brak separacji pamięci w stos/sterty w Javascript. To, co widzisz, może być jednym z następujących:

  1. Rekursja, która działała zbyt głęboko. W takim przypadku musisz przejrzeć algorytm, aby uczynić go bardziej iteracyjnym i użyć mniejszej rekurencji, aby nie trafiać na limity stosu wywołań ograniczeń narzucanych przez przeglądarki.
  2. Jeśli twój algorytm nie ma głębokiej rekursji, może to być po prostu wystarczająco głęboka rozmowa, biorąc pod uwagę, że twój kod jest generowany.
  3. Na koniec, niektóre silniki mogą przydzielać argumenty funkcji i określać zmienne nazwane na jakimś wewnętrznym stosie w celu szybkiego wyszukiwania. Jeśli (lub automatycznie wygenerowany kod) dosłownie używasz tysięcy zmiennych lokalnych lub argumentów w funkcji, może to również spowodować przekroczenie limitów specyficznych dla silnika.
+0

Jestem pewien, że to nie jest problem rekursji (zobacz edycję do pierwotnego pytania). –

+0

Nie ważne jak duża jest tablica, wartość w wywołaniu funkcji jest tylko wskaźnikiem do tej tablicy. Dopóki twoja funkcja dosłownie nie przyjmuje setek argumentów, nie ma to jeszcze nic wspólnego z pamięcią, ponieważ zawartość tablicy nie zostanie przeniesiona do innej pamięci typu specyficznego dla silnika, tylko dlatego, że odwołujesz się do niej podczas rozmowy. –

+0

Masz rację, przynajmniej częściowo. Moja funkcja faktycznie przyjmuje dosłownie setki argumentów (w rzeczywistości miliony). Aby uzyskać szczegółowe informacje, patrz poniżej. Ale to oznacza, że ​​istnieje rozdzielenie pamięci na stos/stertę dla argumentów przekazywanych do wywołania funkcji, przynajmniej dla Safari i Chrome. –

27

OK, wymyśliłem problem. Naprawdę nie było rekursji w moim kodzie. Możliwe jest wywoływanie funkcji JavaScript z setkami argumentów, jeśli są to funkcje "varargs", takie jak na przykład <array>.splice(...), który był moim sprawcą.

Na stronie internetowej: GWT implementuje funkcję Java System.arraycopy(...) przy użyciu funkcji łączenia JavaScript w bardziej lub mniej inteligentny sposób.

splice akceptuje dowolną liczbę elementów wejściowych do wstawienia do tablicy docelowej. Jest możliwe, aby przekazać te elementy wejściowe z innej tablicy stosując następujące konstrukt:

var arguments = [index, howmany].concat(elements); 
Arrays.prototype.splice.apply(targetarray, arguments); 

jest to równoznaczne z wywołaniem:

targetarray.splice(index, howmany, elements[0], elements[1], elements[2], ...); 

Jeśli elementy wystąpią duże (patrz poniżej, co „wielki "oznacza dla różnych przeglądarek), możesz może dostać" Przekroczony maksymalny rozmiar stosu połączeń "bez rekursji, ponieważ jego zawartość zostanie załadowana na stos dla wywołania funkcji.

Oto krótki skrypt, który demonstruje ten problem:

var elements = new Array(); 
for (i=0; i<126000; i++) elements[i] = 1; 
try { 
    var arguments = [0, 0].concat(elements); 
    Array.prototype.splice.apply(elements, arguments); 
    alert("OK"); 
} catch (err) { 
    alert(err.message); 
} 

Stosując ten scenariusz, "wielki" oznacza:

  • Chrome 19: Elementy zawiera ~ 125000 numery
  • Safari 5.1 (w systemie Windows): elementy zawierają ~ 65 000 numerów
  • Firefox 12: elementy zawierają ~ 500 000 liczb
  • Opera 11.61: elementy zawierają ~ 1,000,000 liczb

Zwycięzcą jest: Internet Explorer 8 dla odmiany! Może wykorzystać całą pamięć systemową, zanim ta funkcja nie powiedzie się.

Na marginesie: Firefox i Opera faktycznie rzucać inny (bardziej użyteczne) Komunikat o błędzie: funkcji Function.prototype.apply: argArray jest zbyt duża

+3

Ow, wiedziałem, że to możliwe, ale mam nadzieję, że nikt nigdy nie spróbuje tego. –

+1

Zabawne jest to, że Google właściwie to ustandaryzowało w GWT ... :) Jest to prawdopodobnie najszybszy sposób na wdrożenie Java System.arraycopy w JavaScript, ale rozkłada się na duże tablice ... i, chłopcze, był ten błąd ból głowy, aby znaleźć ... –

+0

Uwaga: zostało to naprawione w GWT 2.7: https://gwt.googlesource.com/gwt/+log/master/user/super/com/google/gwt/emul/java/ lang/System.java –

Powiązane problemy