javascript Chrome:如何解决 Math.max.apply(Math, array) 上的“超出最大调用堆栈大小”错误
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18308700/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Chrome: How to solve "Maximum call stack size exceeded" errors on Math.max.apply( Math, array )
提问by PanChan
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.
它在 Firefox 和 IE 上运行良好,但在 Chrome 上我总是Maximum call stack size exceeded
出错……我当前的数组有 221954 个元素,这不是我最大的。
Does someone know how to solve this error on Chrome? How can I optimize the search of max and min value?
有人知道如何在 Chrome 上解决此错误吗?如何优化最大值和最小值的搜索?
For those people who can't believe, try this in the console of Chrome:
不信的小伙伴可以在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
---> RangeError:超出最大调用堆栈大小
回答by t.O
This problem has nothing to do specifically with Math.max and Math.min.
这个问题与 Math.max 和 Math.min 没有特别的关系。
Function.prototype.apply can only receive array of limited length as its second argument.
Function.prototype.apply 只能接收有限长度的数组作为它的第二个参数。
Locally, I tested it in Chrome using:
在本地,我使用以下方法在 Chrome 中对其进行了测试:
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.
在本地,limit(l) 在 l = 124980 时完全崩溃。在 Canary 中,这是另一个数字,但也有 ~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:
这是为什么会发生这种情况的示例解释: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 )。据我了解为什么在 V8 中抛出 RangeError :
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.
V8 在汇编中实现了 Function.prototype.apply。调用该函数之前,应该将所有的函数调用参数,例如thisArg,并且所有第二ARG阵列的成员,一个接一个,入堆栈,调用JavaScript函数之前。但是栈的容量是有限的,如果达到上限,就会得到RangeError。
This is what I've found in V8 source (IA-32 assembly, builtins-ia32.cc):
这是我在 V8 源代码(IA-32 程序集,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):
这是 APPLY_OVERFLOW 函数,用 JS 编写(同样,V8 源代码,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];
回答by Tom
To me the error shouldn't come from the call into Math.min / max it looks like the result of using recursion which I cannot beleive Chrome would use to implement those functions.
对我来说,错误不应该来自对 Math.min / max 的调用,它看起来像是使用递归的结果,我无法相信 Chrome 会用来实现这些函数。
Are they embeded in recursive code?
它们是否嵌入到递归代码中?
You can roll your own min / max code trivially to avoid the problem in Chrome.
您可以轻松地滚动自己的最小/最大代码以避免 Chrome 中的问题。
回答by Fly_pig
var a=[];
for(var i=0;i<1125011;i++){
a[i] = i;
}
function maxIterate(arr){
var max = arr[0];
for(var i = 1;i< arr.length; i++){
(max < arr[i]) && (max = arr[i])
}
return max;
}
console.log(maxIterate(a));
Math.max
may use recursive method to get max value, just rewrite an iterating function to get max instead.This will avoid RangeError.
Math.max
可以使用递归方法来获取最大值,只需重写一个迭代函数来获取最大值。这将避免 RangeError。
回答by lukas.pukenis
You're reaching function parameter size limit. And it is OK. Function should accept only a few parameters as else is a code smell.
您已达到函数参数大小限制。它是OK。函数应该只接受几个参数,否则是代码异味。
If you have a bunch of items? - Use Array. You are using .apply()
which passes
arguments like: fun(1,2,3,4,5,6....)
and reaches the limit. This is bad practice.
如果你有一堆物品?- 使用数组。您正在使用.apply()
which 传递如下参数:fun(1,2,3,4,5,6....)
并达到限制。这是不好的做法。
The problem is - Math.max()
is wired to work only like this, so your best bet would be iterative search function. But that's another topic, as performance and algorithm may vary for example if you first sort the array.
问题是 -Math.max()
只能像这样工作,所以最好的选择是迭代搜索功能。但这是另一个话题,因为性能和算法可能会有所不同,例如,如果您首先对数组进行排序。