【问题标题】:Maximum call stack size exceeded error超出最大调用堆栈大小错误
【发布时间】:2011-08-31 00:46:51
【问题描述】:

我正在使用 Direct Web Remoting (DWR) JavaScript 库文件,并且仅在 Safari(台式机和 iPad)中出现错误

上面写着

超过最大调用堆栈大小。

这个错误到底是什么意思,它会完全停止处理吗?

还有对Safari 浏览器的任何修复(实际上是在iPad Safari 上,上面写着

JS:执行超时

我假设是相同的调用堆栈问题)

【问题讨论】:

  • 我在尝试通过 ajax 中的 data 发送变量(未声明它们)时遇到此错误。通过声明变量修复了错误。
  • 无限循环...
  • 对我来说,我的 JavaScript 函数的名称是 onclick(),我得到了这个错误 :-) 虽然这些是保留名称

标签: javascript html webkit callstack dwr


【解决方案1】:

这意味着在您的代码中的某个地方,您正在调用一个函数,该函数又调用另一个函数,依此类推,直到您达到调用堆栈限制。

这几乎总是因为具有未满足基本情况的递归函数。

查看堆栈

考虑这段代码...

(function a() {
    a();
})();

这是几次调用后的堆栈...

如您所见,调用堆栈会不断增长,直到达到极限:浏览器硬编码堆栈大小或内存耗尽。

为了修复它,请确保您的递归函数具有能够满足的基本情况...

(function a(x) {
    // The following condition 
    // is the base case.
    if ( ! x) {
        return;
    }
    a(--x);
})(10);

【讨论】:

  • 为此非常感谢..那么有什么方法/工具可以让我用尽堆栈限制..再次,因为这是一个巨大的库文件,而不是我创建的,我不完全知道哪里出了问题..
  • @Oliver 如果你必须运行一个函数近 300 万亿次,你可能做错了什么。
  • @Oliver - 你可能想研究动态编程 - 我猜你没有那么多独特的解决方案,你在重复步骤,所以你可能需要将它编程为多项式时间而不是二次方。 en.wikipedia.org/wiki/Dynamic_programming
  • @Akhoy 在常规函数中是的,但请考虑递归函数。它调用另一个(自身)并将前者的返回地址留在堆栈上(否则 PC 会知道去哪里?)这当然会再次调用自身,每次将返回地址压入堆栈。当由于下个世纪不太可能满足的基本情况而导致这种情况消失时,您会遇到堆栈溢出。
  • 您的代码运行相同的低效段 134217728 次。不开玩笑,它很慢。您应该使用按位运算符,这样您就可以将 9 级循环展开为一级循环。
【解决方案2】:

如果您不小心两次导入/嵌入同一个 JavaScript 文件,有时会出现这种情况,值得在检查器的资源选项卡中检查。

【讨论】:

  • 这个问题通常发生在你不小心两次导入/嵌入同一个JS文件时。我不想推测导致递归循环的确切过程,但我认为这就像以 100 英里/小时的速度驾驶两辆相同的汽车通过同一个收费站。
  • 我认为情况并非如此。如果嵌入了两个相同类型的函数或变量,后者将覆盖之前的定义。
  • 是的......我真的不知道它为什么会发生或过程是什么,尽管它也会覆盖。
  • 晚会有点晚了,但可以想象,当代码在任何函数之外时,两者都将在两个 JS 文件中执行,并且一个可能不会相互覆盖,因为它们在外部任何功能。
  • @lucygenik 下次您在批准对您的帖子进行巨魔编辑时要更加小心。这篇帖子差点被当作垃圾邮件删除(查看历史记录)
【解决方案3】:

在我的例子中,我发送的是输入元素而不是它们的值:

$.post( '',{ registerName: $('#registerName') } )

代替:

$.post( '',{ registerName: $('#registerName').val() } )

这冻结了我的 Chrome 选项卡,以至于当页面变得无响应时,它甚至没有显示“等待/终止”对话框...

【讨论】:

  • 我想知道为什么这样的错误没有例外...谢谢!
  • 非常感谢,这救了我
  • 我正要添加这个作为可能的解决方案。我只是做了这件事,哈哈
  • “我想知道为什么这样的错误没有异常......”因为它是javascript。
  • 谢谢。这就是问题
【解决方案4】:

在您的代码中某处存在递归循环(即一个函数最终会一次又一次地调用自身,直到堆栈已满)。

其他浏览器要么有更大的堆栈(因此你会得到一个超时),要么他们出于某种原因吞下了错误(可能是放置不当的 try-catch)。

当错误发生时,使用调试器检查调用堆栈。

【讨论】:

  • 非常感谢您的回复。在 IE/FF 上,代码似乎运行良好..仅在桌面 Safari 和 iPad Safari 中,我得到了错误。实际上 JS 文件不是我自己创建的,而是一个库文件(来自 DWR engine.js)..directwebremoting.org 另外,当您说“调试器检查调用堆栈”时,我该如何使用 Safari Inspector 来做到这一点?
  • 我没有使用 Safari Inspector 的经验。尝试打开 JavaScript 调试器并加载您的页面。当抛出未捕获的错误时,调试器应该停止。如果这不起作用,请询问超级用户或网站管理员(见页面底部)
  • 有两个函数名称相同!! DOH!
【解决方案5】:

检测堆栈溢出的问题是有时堆栈跟踪会展开,您将无法看到实际发生的情况。

我发现 Chrome 的一些较新的调试工具对此很有用。

点击Performance tab,确保Javascript samples 已启用,你会得到类似的东西。

溢出在哪里很明显!如果您单击extendObject,您将能够实际看到代码中的确切行号。

您还可以查看可能有用或无用的时间安排或红鲱鱼。


如果您实际上找不到问题,另一个有用的技巧是在您认为问题所在的地方放置大量 console.log 语句。上面的上一步可以帮助您解决这个问题。

在 Chrome 中,如果您重复输出相同的数据,它会像这样显示它,从而更清楚地显示问题所在。在这种情况下,堆栈在最终崩溃之前达到了 7152 帧:

【讨论】:

  • 对于任何阅读此答案的人。当然它对我有用,我能够将其缩小为递归函数调用。我可以使用“性能”选项卡轻松地立即跟踪问题。这是由于 2 个角度元素资产包含在一个单独的应用程序中。一旦我禁用了其中一个元素,问题就消失了。希望这对某人有帮助!
【解决方案6】:

在我的例子中,我使用以下方法将大字节数组转换为字符串:

String.fromCharCode.apply(null, new Uint16Array(bytes))

bytes 包含数百万个条目,太大而无法放入堆栈。

【讨论】:

  • 我在同一行也有同样的问题!!谢谢
  • 那么转换大字节数组的最佳解决方案是什么??
  • @KarthikHande 您必须在 for 循环中而不是一次调用中执行此操作。 var uintArray = new Uint16Array(bytes); var converted = []; uintArray.forEach(function(byte) {converted.push(String.fromCharCode(byte))});
  • 如何将其转换为 JSON?我尝试了 JSON.parse 但它不起作用...我正在使用 Uintl8Array
  • @KarthikHande 如果没有看到整个问题,我无法判断。请打开另一个包含所有详细信息的问题。
【解决方案7】:

在我的例子中,点击事件是在子元素上传播的。所以,我不得不把以下内容:

e.stopPropagation()

点击事件:

 $(document).on("click", ".remove-discount-button", function (e) {
           e.stopPropagation();
           //some code
        });
 $(document).on("click", ".current-code", function () {
     $('.remove-discount-button').trigger("click");
 });

这里是html代码:

 <div class="current-code">                                      
      <input type="submit" name="removediscountcouponcode" value="
title="Remove" class="remove-discount-button">
   </div>

【讨论】:

    【解决方案8】:

    检查 Chrome 开发工具栏控制台中的错误详细信息,这将为您提供调用堆栈中的函数,并引导您找到导致错误的递归。

    【讨论】:

      【解决方案9】:

      如果您出于某种原因需要运行无限进程/递归,您可以在单独的线程中使用 webworker。 http://www.html5rocks.com/en/tutorials/workers/basics/

      如果要操作 dom 元素并重绘,请使用动画 http://creativejs.com/resources/requestanimationframe/

      【讨论】:

        【解决方案10】:

        这也可能导致Maximum call stack size exceeded 错误:

        var items = [];
        [].push.apply(items, new Array(1000000)); //Bad
        

        这里也一样:

        items.push(...new Array(1000000)); //Bad
        

        来自Mozilla Docs

        但请注意:以这种方式使用 apply 时,可能会超出 JavaScript 引擎的参数长度限制。的后果 应用一个参数太多的函数(想想几十个 数以千计的参数)因引擎而异(JavaScriptCore 有 硬编码的参数限制为 65536),因为限制(实际上甚至 任何过大堆栈行为的性质)未指定。 有些引擎会抛出异常。更有害的是,其他人会 任意限制实际传递给 应用函数。为了说明后一种情况:如果这样的引擎 有四个参数的限制(实际限制当然是 明显更高),就好像论点 5、6、2、3 有 已通过应用在上面的示例中,而不是完整的 数组。

        那就试试吧:

        var items = [];
        var newItems = new Array(1000000);
        for(var i = 0; i < newItems.length; i++){
          items.push(newItems[i]);
        }
        

        【讨论】:

          【解决方案11】:

          就我而言,我基本上忘记获取inputvalue

          错误

          let name=document.getElementById('name');
          param={"name":name}
          

          正确

          let name=document.getElementById('name').value;
          param={"name":name}
          

          【讨论】:

          • 类似于@FKasa,这是在javascript中
          • 正是发生在我身上的事情 - 我不小心发送了整个输入字段,而不仅仅是字段的值。
          【解决方案12】:

          这里几乎每个答案都表明这只能由无限循环引起。这不是真的,否则您可能会通过深度嵌套调用超出堆栈运行(并不是说这很有效,但它肯定在可能的范围内)。如果您可以控制 JavaScript VM,则可以调整堆栈大小。例如:

          node --stack-size=2000

          另请参阅:How can I increase the maximum call stack size in Node.js

          【讨论】:

            【解决方案13】:

            我们最近在我们正在开发的管理站点中添加了一个字段 - contact_type...简单吧?好吧,如果你调用 select "type" 并尝试通过 jquery ajax 调用发送它会失败,这个错误深埋在 jquery.js 不要这样做:

            $.ajax({
                dataType: "json",
                type: "POST",
                url: "/some_function.php",
                data: { contact_uid:contact_uid, type:type }
            });
            

            问题在于 type:type - 我相信是我们将参数命名为“type” - 有一个名为 type 的值变量不是问题。我们将其更改为:

            $.ajax({
                dataType: "json",
                type: "POST",
                url: "/some_function.php",
                data: { contact_uid:contact_uid, contact_type:type }
            });
            

            并相应地重写了 some_function.php - 问题解决了。

            【讨论】:

            • 感谢您撰写此答案。实际上,我发现我没有创建要发布到服务器端脚本的变量。
            【解决方案14】:

            在我的例子中,两个 jQuery 模态显示堆叠在一起。防止这解决了我的问题。

            【讨论】:

              【解决方案15】:

              检查你是否有一个调用自身的函数。例如

              export default class DateUtils {
                static now = (): Date => {
                  return DateUtils.now()
                }
              }
              

              【讨论】:

                【解决方案16】:

                对于我作为 TypeScript 的初学者来说,这是 _var1 的 getter 和 setter 的问题。

                class Point2{
                    
                    constructor(private _var1?: number, private y?: number){}
                
                    set var1(num: number){
                        this._var1 = num  // problem was here, it was this.var1 = num
                    }
                    get var1(){
                        return this._var1 // this was return this.var1
                    }
                }
                

                【讨论】:

                  【解决方案17】:
                  dtTable.dataTable({
                      sDom: "<'row'<'col-sm-6'l><'col-sm-6'f>r>t<'row'<'col-sm-6'i><'col-sm-6'p>>",
                      "processing": true,
                      "serverSide": true,
                      "order": [[6, "desc"]],
                      "columnDefs": [
                          {className: "text-right", "targets": [2, 3, 4, 5]}
                      ],
                      "ajax": {
                          "url": "/dt",
                          "data": function (d) {
                              d.loanRef = loanRef;
                          }
                      },
                      "fnRowCallback": function (nRow, aData, iDisplayIndex, iDisplayIndexFull) {
                          var editButton = '';
                          // The number of columns to display in the datatable
                          var cols = 8;
                          // Th row element's ID
                          var id = aData[(cols - 1)];
                      }
                  });
                  

                  在上面的 data 函数中,我使用了相同的名称 d.loanRef = loanRef 但没有创建变量 loanRef 因此递归地声明自己。

                  解决方案:声明一个loanRef 变量,或者更好的是,使用与d.loanRef 上使用的名称不同的名称。

                  【讨论】:

                    【解决方案18】:

                    发送输入元素而不是它们的值很可能会像 FK 提到的那样解决它

                    【讨论】:

                      【解决方案19】:

                      以下相同代码的两个调用如果减 1 则在我的计算机上的 Chrome 32 中工作,例如17905 vs 17904。如果按原样运行,它们将产生错误“RangeError:超出最大调用堆栈大小”。这个限制似乎不是硬编码的,而是取决于您机器的硬件。看起来,如果作为函数调用,这个自我强加的限制高于作为方法调用,即这个特定代码在作为函数调用时使用的内存更少。

                      作为方法调用:

                      var ninja = {
                          chirp: function(n) {
                              return n > 1 ? ninja.chirp(n-1) + "-chirp" : "chirp";
                          }
                      };
                      
                      ninja.chirp(17905);
                      

                      作为函数调用:

                      function chirp(n) {
                          return n > 1 ? chirp( n - 1 ) + "-chirp" : "chirp";
                      }
                      
                      chirp(20889);
                      

                      【讨论】:

                        【解决方案20】:

                        我也遇到了类似的问题,这里是详细信息 使用下拉徽标上传框上传徽标时

                        <div>
                              <div class="uploader greyLogoBox" id="uploader" flex="64" onclick="$('#filePhoto').click()">
                                <img id="imageBox" src="{{ $ctrl.companyLogoUrl }}" alt=""/>
                                <input type="file" name="userprofile_picture"  id="filePhoto" ngf-select="$ctrl.createUploadLogoRequest()"/>
                                <md-icon ng-if="!$ctrl.isLogoPresent" class="upload-icon" md-font-set="material-icons">cloud_upload</md-icon>
                                <div ng-if="!$ctrl.isLogoPresent" class="text">Drag and drop a file here, or click to upload</div>
                              </div>
                              <script type="text/javascript">
                                  var imageLoader = document.getElementById('filePhoto');
                                  imageLoader.addEventListener('change', handleImage, false);
                        
                                  function handleImage(e) {
                                      var reader = new FileReader();
                                      reader.onload = function (event) {
                        
                                          $('.uploader img').attr('src',event.target.result);
                                      }
                                      reader.readAsDataURL(e.target.files[0]);
                                  }
                              </script>
                              </div>
                        

                        CSS.css

                        .uploader {
                          position:relative;
                          overflow:hidden;
                          height:100px;
                          max-width: 75%;
                          margin: auto;
                          text-align: center;
                        
                          img{
                            max-width: 464px;
                            max-height: 100px;
                            z-index:1;
                            border:none;
                          }
                        
                          .drag-drop-zone {
                            background: rgba(0, 0, 0, 0.04);
                            border: 1px solid rgba(0, 0, 0, 0.12);
                            padding: 32px;
                          }
                        }
                        
                        .uploader img{
                          max-width: 464px;
                          max-height: 100px;
                          z-index:1;
                          border:none;
                        }
                        
                        
                        
                        .greyLogoBox {
                          width: 100%;
                          background: #EBEBEB;
                          border: 1px solid #D7D7D7;
                          text-align: center;
                          height: 100px;
                          padding-top: 22px;
                          box-sizing: border-box;
                        }
                        
                        
                        #filePhoto{
                          position:absolute;
                          width:464px;
                          height:100px;
                          left:0;
                          top:0;
                          z-index:2;
                          opacity:0;
                          cursor:pointer;
                        }
                        

                        在更正之前我的代码是:

                        function handleImage(e) {
                                      var reader = new FileReader();
                                      reader.onload = function (event) {
                                          onclick="$('#filePhoto').click()"
                                          $('.uploader img').attr('src',event.target.result);
                                      }
                                      reader.readAsDataURL(e.target.files[0]);
                                  }
                        

                        控制台报错:

                        我通过从 div 标签中删除 onclick="$('#filePhoto').click()" 解决了这个问题。

                        【讨论】:

                        • 你在哪里添加了click()
                        • 我想回答但系统建议先浏览旧的。是的,我的 HTML 中也有这个 onclick="$obj.click()"。看起来它以某种方式循环 jquery 附件
                        【解决方案21】:

                        我遇到了同样的问题 我已经通过删除在 ajax 上使用过两次的字段名称来解决它 例如

                            jQuery.ajax({
                            url : '/search-result',
                            data : {
                              searchField : searchField,
                              searchFieldValue : searchField,
                              nid    :  nid,
                              indexName : indexName,
                              indexType : indexType
                            },
                        .....
                        

                        【讨论】:

                          【解决方案22】:

                          我的问题是因为我的子路由与父路由相同:

                          const routes: Routes = [
                            {
                              path: '',
                              component: HomeComponent,
                              children: [
                                { path: '', redirectTo: 'home', pathMatch: 'prefix' },
                                { path: 'home', loadChildren: './home.module#HomeModule' },
                              ]
                            }
                          ];
                          

                          所以我不得不删除孩子路线的线

                          const routes: Routes = [
                            {
                              path: '',
                              component: HomeComponent,
                              children: [
                                { path: 'home', loadChildren: './home.module#HomeModule' },
                              ]
                            }
                          ];
                          

                          【讨论】:

                          • 是的。我用这个解决了。我在 Angular 11 路线中进行了递归
                          【解决方案23】:

                          在我的例子中,我有 2 个同名的变量!

                          【讨论】:

                            【解决方案24】:

                            你可以在 crome 浏览器中找到你的递归函数,按 ctrl+shift+j 然后 source tab,它给你代码编译流程,你可以在代码中找到使用断点。

                            【讨论】:

                            • 然而,有时很难找出错误的来源。我们的网站上有很多 JavaScript。
                            【解决方案25】:

                            我知道这个帖子很旧,但我认为值得一提的是我发现这个问题的场景,以便它可以帮助其他人。

                            假设你有这样的嵌套元素:

                            <a href="#" id="profile-avatar-picker">
                                <span class="fa fa-camera fa-2x"></span>
                                <input id="avatar-file" name="avatar-file" type="file" style="display: none;" />
                            </a>
                            

                            您不能在其父事件中操纵子元素事件,因为它会传播到自身,进行递归调用,直到抛出异常。

                            所以这段代码会失败:

                            $('#profile-avatar-picker').on('click', (e) => {
                                e.preventDefault();
                            
                                $('#profilePictureFile').trigger("click");
                            });
                            

                            您有两种选择可以避免这种情况:

                            • 将子级移到父级之外。
                            • stopPropagation 函数应用于子元素。

                            【讨论】:

                              【解决方案26】:

                              我有这个错误是因为我有两个同名的 JS 函数

                              【讨论】:

                                【解决方案27】:

                                如果您使用的是谷歌地图,请检查传递到new google.maps.LatLng 的纬度 lng 是否具有正确的格式。在我的情况下,它们被传递为未定义。

                                【讨论】:

                                  【解决方案28】:

                                  有时由于转换数据类型而发生,例如您有一个您认为是字符串的对象。

                                  以js客户端为例,nodejs中的socket.id不是字符串。要将其用作字符串,您必须在之前添加单词 String:

                                  String(socket.id);
                                  

                                  【讨论】:

                                    【解决方案29】:

                                    在我的情况下,我在 ajax 调用时收到此错误,并且我尝试传递该变量的数据尚未定义,这向我显示了此错误,但未描述未定义的变量。我添加了定义变量 n 的值。

                                    【讨论】:

                                    • 我自己也以类似的方式被烧毁,就我而言,这是因为我输入了一个变量名……实际上是一样的。
                                    【解决方案30】:

                                    我试图分配一个变量,一个值,而该变量尚未声明。

                                    声明变量修复了我的错误。

                                    【讨论】:

                                      猜你喜欢
                                      • 1970-01-01
                                      • 2019-07-19
                                      • 1970-01-01
                                      • 1970-01-01
                                      • 2021-09-21
                                      • 2021-08-15
                                      • 2016-09-26
                                      • 2013-08-19
                                      • 2021-12-05
                                      相关资源
                                      最近更新 更多