Chrome:如何解决“超出最大调用堆栈大小” Math.max.apply上的错误(数学,数组) [英] Chrome: How to solve "Maximum call stack size exceeded" errors on Math.max.apply( Math, array )

查看:176
本文介绍了Chrome:如何解决“超出最大调用堆栈大小” Math.max.apply上的错误(数学,数组)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须找到非常大的数组的最大值和最小值。为此,我使用

  Math.max.apply(Math,my_array); 
Math.min.apply(Math,my_array);

它适用于Firefox和IE,但在Chrome上我总是得到 Maximum调用堆栈大小超过错误...我当前的数组有221954个元素,这不是我最大的元素。



有人知道如何解决这个问题吗? Chrome上出现错误?如何优化搜索最大值和最小值?



对于那些无法相信的人,请在Chrome的控制台中试用:

$ b ($ i

pre $ var xxx = []
(i = 0; i <300000; i ++){
xxx.push 。随机());
}
Math.max.apply(Math,xxx);

---> RangeError:超出最大调用堆栈大小

解决方案

这个问题与Math.max和Math.min没有任何关系。

Function.prototype。


$ b 在本地,我在Chrome中使用以下方式对其进行了测试:

 函数限制(l){
var x = []; x.length = 1;
(function(){})。apply(null,x);





$ b

在本地,limit(l)与l = 124980完全一致。在金丝雀里,这是另一个数字,但也是〜125k。



这是为什么发生这种情况的一个示例说明: https://code.google.com/p/v8/issues/detail?id=2896 (它也可以在其他版本中重新编译JS引擎,例如MDN提到了这个问题: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Using_apply_and_built-in_functions (以但要当心...... ),在WebKit bugzilla中指出这个问题: https://bugs.webkit.org/ show_bug.cgi?ID = 80797 )。据我了解,为什么RangeError在V8中引发:



V8在汇编中实现了Function.prototype.apply。在调用函数之前,它应该放置所有函数调用参数,例如thisArg和第二个arg数组中的所有成员一个,在调用javascript函数之前将它们放到堆栈中。但是堆栈的容量有限,如果达到了限制,你会得到RangeError。



这是我在V8源代码中找到的(IA-32汇编,
$ b $ pre $ void Builtins :: Generate_FunctionApply(MacroAssembler * masm){
static const int kArgumentsOffset = 2 * kPointerSize;
static const int kReceiverOffset = 3 * kPointerSize;
static const int kFunctionOffset = 4 * kPointerSize;
{
FrameScope frame_scope(masm,StackFrame :: INTERNAL);

__ push(操作数(ebp,kFunctionOffset)); // push this
__ push(Operand(ebp,kArgumentsOffset)); //推入参数
__ InvokeBuiltin(Builtins :: APPLY_PREPARE,CALL_FUNCTION);

//检查堆栈是否溢出。我们不试图在这里捕获
//中断(例如,调试中断和抢占),因此将检查真实堆栈
//限制。
标签没问题;
ExternalReference real_stack_limit =
ExternalReference :: address_of_real_stack_limit(masm-> isolate());
__ mov(edi,Operand :: StaticVariable(real_stack_limit));
//使ecx成为我们留下的空间。堆栈可能已经溢出了
//这将导致ecx变为负值。
// !!添加评论:IA-32堆栈向下增长,如果地址到它的当前顶端是0,那么它不能再放入更多元素。 esp是指向栈顶的指针。
__ mov(ecx,esp);
// !!添加评论:edi拥有real_stack_limit,它拥有堆栈不应超出的最小地址。如果我们从ecx中减去edi(= esp,或者换句话说,堆栈剩下多少空间),我们可能会得到一个负值,上面的注释表示
__ sub(ecx, EDI);
//当edx展开到
//堆栈时,让edx成为数组需要的空间。
// !!添加评论:eax保存这个应用调用的参数数量,其中第二个参数数组的每个成员计为单独的参数
__ mov(edx,eax);
// !!添加评论:kPointerSizeLog2 - kSmiTagSize是1参数需要多少空间的基数2的对数。通过shl,我们实际上获得了2 ^(kPointerSizeLog2 - kSmiTagSize)* arguments_count,即实际参数占用了多少空间
__ shl(edx,kPointerSizeLog2 - kSmiTagSize);
//检查参数是否会溢出堆栈。
// !!添加评论:我们比较了ecx,它是我们可以将多少数据放到堆栈中,而现在我们需要将多少数据放到堆栈中。
__ j(大,&好吧); //签名比较

//堆栈空间不足。
__ push(操作数(ebp,4 * kPointerSize)); //推这个
__ push(eax);
__ InvokeBuiltin(Builtins :: APPLY_OVERFLOW,CALL_FUNCTION);

请检查!



这是APPLY_OVERFLOW函数,用JS编写(再次,V8源码,runtime.js):

 函数APPLY_OVERFLOW(length){
throw%MakeRangeError('stack_overflow',[]);

编辑:比如:

  var max = -Infinity;如果(arr [i]> max)max = arr [i],则为
(var i = 0; i< arr.length; i ++)。


I have to find the maximum and minimum value of very huge arrays. For this I'm using

Math.max.apply(Math, my_array);
Math.min.apply(Math, my_array);

It works good on Firefox and IE, but on Chrome I always get Maximum call stack size exceeded errors... my current array has 221954 elements, and that's not my biggest.

Does someone know how to solve this error on Chrome? How can I optimize the search of max and min value?

For those people who can't believe, try this in the console of Chrome:

var xxx = []
for(var i=0; i<300000; i++){
    xxx.push(Math.random());
}
Math.max.apply(Math, xxx);

---> RangeError: Maximum call stack size exceeded

解决方案

This problem has nothing to do specifically with Math.max and Math.min.

Function.prototype.apply can only receive array of limited length as its second argument.

Locally, I tested it in Chrome using:

function limit(l) {
  var x = []; x.length = l;
  (function (){}).apply(null, x);
}

Locally, limit(l) crashed exactly with l = 124980. In canary, that was another number, but also ~125k.

This is an example explanation of why that happens: https://code.google.com/p/v8/issues/detail?id=2896 (it is also reprodusible in other JS engines, for example MDN has a mention of the issue: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Using_apply_and_built-in_functions (Starting with "But beware..."), pointing to this issue in WebKit bugzilla: https://bugs.webkit.org/show_bug.cgi?id=80797). As far as I understand why RangeError is thrown in V8:

V8 implements Function.prototype.apply in assembly. Before calling the function, it should place all function call parameters, e.g. thisArg, and all of the members of 2nd arg array, one by one, onto stack, before calling the javascript function. But the stack has limited capacity, and if you hit the limit, you will get RangeError.

This is what I've found in V8 source (IA-32 assembly, builtins-ia32.cc):

void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
  static const int kArgumentsOffset = 2 * kPointerSize;
  static const int kReceiverOffset = 3 * kPointerSize;
  static const int kFunctionOffset = 4 * kPointerSize;
  {
    FrameScope frame_scope(masm, StackFrame::INTERNAL);

    __ push(Operand(ebp, kFunctionOffset));  // push this
    __ push(Operand(ebp, kArgumentsOffset));  // push arguments
    __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);

    // Check the stack for overflow. We are not trying to catch
    // interruptions (e.g. debug break and preemption) here, so the "real stack
    // limit" is checked.
    Label okay;
    ExternalReference real_stack_limit =
        ExternalReference::address_of_real_stack_limit(masm->isolate());
    __ mov(edi, Operand::StaticVariable(real_stack_limit));
    // Make ecx the space we have left. The stack might already be overflowed
    // here which will cause ecx to become negative.
    // !! ADDED COMMENT: IA-32 stack grows downwards, if address to its current top is 0 then it cannot be placed any more elements into. esp is the pointer to stack top.
    __ mov(ecx, esp);
    // !! ADDED COMMENT: edi holds the "real_stack_limit", which holds the  minimum address that stack should not grow beyond. If we subtract edi from ecx (=esp, or, in other words, "how much space is left on the stack"), we may get a negative value, and the comment above says that
    __ sub(ecx, edi);
    // Make edx the space we need for the array when it is unrolled onto the
    // stack.
    // !! ADDED COMMENT: eax holds the number of arguments for this apply call, where every member of the 2nd argument array counts as separate argument
    __ mov(edx, eax);
    // !! ADDED COMMENT: kPointerSizeLog2 - kSmiTagSize is the base-2-logarithm of how much space would 1 argument take. By shl we in fact get 2^(kPointerSizeLog2 - kSmiTagSize) * arguments_count, i.e. how much space do actual arguments occupy
    __ shl(edx, kPointerSizeLog2 - kSmiTagSize);
    // Check if the arguments will overflow the stack.
    // !! ADDED COMMENT: we compare ecx which is how much data we can put onto stack with edx which now means how much data we need to put onto stack
    __ cmp(ecx, edx);
    __ j(greater, &okay);  // Signed comparison.

    // Out of stack space.
    __ push(Operand(ebp, 4 * kPointerSize));  // push this
    __ push(eax);
    __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);

Please check !! ADDED COMMENT for explanations of how I understand it.

And this is APPLY_OVERFLOW function, written in JS (again, V8 source, runtime.js):

function APPLY_OVERFLOW(length) {
  throw %MakeRangeError('stack_overflow', []);
}

EDIT: In your case, I would go like:

var max = -Infinity; 
for(var i = 0; i < arr.length; i++ ) if (arr[i] > max) max = arr[i];

这篇关于Chrome:如何解决“超出最大调用堆栈大小” Math.max.apply上的错误(数学,数组)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆