在此 Windows 小工具中查找导致内存泄漏的原因

Find what is causing a memory leak in this Windows Gadget

本文关键字:泄漏 内存 Windows 工具 查找 在此      更新时间:2023-09-26

场景

我正在将Windows小工具平台与此小工具一起使用:

http://win7gadgets.com/pc-system/sushis_driveinfo.html

问题

该小工具

有内存泄漏,如果我继续运行这个小工具+24小时。 它可以将RAM消耗增加到1 GB,而其他类似的小工具不会证明这一点,所以我丢弃了这是一个侧边栏.exe 内存管理不是脚本错误。

当运行小工具的时间更长时,小工具的无响应性会更多(单击时(。

我对 JavaScript 的了解是 NULL,但无论如何我可以理解语法并尝试理解开发人员在此代码中正在做什么,我认为问题在于管理图像对象时,但在我看来,这些对象似乎在每次操作后都得到了适当的处理。

问题

这是小工具源。

有人可以帮助我发现并修复导致此小工具内存泄漏的原因吗?

sushi_driveinfo.html(主窗口(:

<html>
  <head>
    <title>Drive Info</title>
    <style>
      body { margin: 0; padding: 0; width: 156px; height: 200px; background-image: url(images'canvas.png); color: #ffffff; font-family: 'Segoe UI'; }
      #targets { position: absolute; top: 0; left: 0; }
      .target { position: absolute; width: 156px; height: 48; left: 0; cursor: hand; }
    </style>
    <script type="text/javascript">
      var lst = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
      var timeout = null;
      var drives = new Array(26);
      var drvchk = new Array(26);
      var drvspc = new Array(26);
      var vizchg = false;
      var current_y = 0;
      var background,theme,remove,local,network,media,show_pc,show_net;
      var item_height=48;
      var icon_offset=20;
      var text_offset=72;
      var meter_offset=24;
      function convertBytes(b)
      {
        var i = 0, u = Array(' MB', ' GB', ' TB');
        while (b >= 1024 && (b /= 1024) >= 1) i++;
        return (Math.round(b * 100) / 100) + u[i];
      }
      function openDrive()
      {        
        var d = window.event.srcElement.getAttribute('drive');    
        System.Shell.execute(d + ':''');
        return;
      }
      function openNetwork()
      {        
        System.Shell.execute("Explorer", "/N,::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}");
        return;
      }
      function openComputer()
      {        
        System.Shell.execute("Explorer", "/N,::{20D04FE0-3AEA-1069-A2D8-08002B30309D}");
        return;
      }
      function recheckDrives() {
       for(var i = 0; i < 26; i++)
        {
          if (!drives[i]) {
            drives[i] = System.Shell.drive(lst.charAt(i));
            if (drives[i]) { vizchg = true; drvchk[i] = true; }
          } else {
          if (drives[i].isReady != drvchk[i]) { drvchk[i] = !drvchk[i]; vizchg = true; }
          if (drives[i].isReady && drives[i].freeSpace != drvspc[i]) { drvspc[i] = drives[i].freeSpace; vizchg = true; }
          }
        }
      }
      function calcHeight(h) {
        var y=0;
        if(show_pc==2) y+=h;
        if(show_net==2) y+=h;
        for(var i=0;i<26;i++)
            if(isDriveVisible(i)) y+=h;
        return y;
      }
      function isDriveVisible(i) {
        if(drvchk[i]) {
           if      (drives[i].driveType == 2 && remove == 1)  ;
           else if (drives[i].driveType == 3 && local == 1)   ;
           else if (drives[i].driveType == 4 && network == 1) ;
           else if (drives[i].driveType == 5 && media == 1)   ;
           else if (drives[i].driveType == 1 || drives[i].driveType == 6) ;
           else 
            return true;
        }
        return false;
      }
      function paintPC() {
        if (show_pc == 2) {
            canvas.addImageObject('images/backgrounds/background' + background + 's.png', 0, current_y);  
            var di=canvas.addImageObject('images/drives/pc'+ theme +'.png', icon_offset, current_y);
            di.width*=0.8;
            di.height*=0.8;
            canvas.addTextObject('Computer', 'Segoe UI', 11, 'white', text_offset, current_y + 5);
            var b = document.createElement('DIV');
            b.className = 'target';
            b.style.posTop = current_y;
            b.onclick = openComputer;
            targets.appendChild(b);
            current_y+=item_height;
        }
        return;
      }
      function paintNET() {
        if (show_net == 2) {
            canvas.addImageObject('images/backgrounds/background' + background + 's.png', 0, current_y);  
            var di=canvas.addImageObject('images/drives/net'+ theme +'.png', icon_offset, current_y);
            di.width*=0.8;
            di.height*=0.8;
            canvas.addTextObject('Network', 'Segoe UI', 11, 'white', text_offset, current_y + 5);
            var b = document.createElement('DIV');
            b.className = 'target';
            b.style.posTop = current_y;
            b.onclick = openNetwork;
            targets.appendChild(b);
            current_y+=item_height;
        }
        return;
      }
      function paintGadget()
      {       
      try {
        recheckDrives();
        if (!vizchg) return;
        var total_height=calcHeight(item_height);
        System.Gadget.beginTransition();
        document.body.style.height=total_height;
        canvas.style.height=total_height;
        canvas.removeObjects();
        targets.innerHtml = '';
        current_y = 0;
        paintPC();
        paintNET();
        for(i = 0; i < 26; i++)
        {
            if(isDriveVisible(i)) {
              if (drives[i].freeSpace != 0) {
               canvas.addImageObject('images/backgrounds/background' + background + '.png', 0, current_y);  
               var f = Math.round(drives[i].freeSpace / drives[i].totalSize * 100);
               var u = (100 - f); 
               canvas.addTextObject(convertBytes(drives[i].freeSpace) + ' / ' + f + '%', 'Segoe UI', 10, 'white', text_offset, current_y + 17);
               var m = canvas.addImageObject('images/meter' + (u < 90 ? 'blue': (u < 98 ? 'orange': 'red')) + '.png', meter_offset, current_y + 34);   
               m.width = Math.floor((u * 128 / 100));
               m.left = 24 - Math.floor(((128 - m.width) / 2));
              } else {
               canvas.addImageObject('images/backgrounds/background' + background + 's.png', 0, current_y);  
               canvas.addTextObject(convertBytes(drives[i].totalSize), 'Segoe UI', 10, 'white', text_offset, current_y + 17);
              }
              var di=canvas.addImageObject('images/drives/drive' + drives[i].driveType + theme + '.png', icon_offset, current_y-5);
              di.width*=0.8;
              di.height*=0.8;
              canvas.addTextObject(drives[i].volumeLabel + ' (' + drives[i].driveLetter + ':)', 'Segoe UI', 11, 'white', text_offset, current_y + 5);
              var o = document.createElement('DIV');
              o.className = 'target';
              o.style.posTop = current_y;
              o.setAttribute('drive', drives[i].driveLetter);
              o.onclick = openDrive;
              targets.appendChild(o);
              current_y += item_height;
           }
        System.Gadget.endTransition(System.Gadget.TransitionType.morph,0.1);
        window.setTimeout(fixCanvasBackground, 600);
        }
        } finally {
         vizchg = false;
         return;
        }
      }
      function fixCanvasBackground() {
        canvas.src = canvas.src;
      } 
      function initDrives()
      {
       for(var i = 0; i < 26; i++)  {
            drives[i] = System.Shell.drive(lst.charAt(i));
            if (drives[i] && drives[i].isReady)
            { drvchk[i] = true ; drvspc[i] = drives[i].freeSpace; }
            else  { drvchk[i] = false; }
        }
        return;
      }
      function onShowSettings() {
        window.clearInterval(timeout);
        System.Gadget.beginTransition();
        window.setTimeout(endTransitionFast, 400);
      }
      function onSettingsClosed() {
        readSettings();
        timeout=window.setInterval(paintGadget, 2500);
        vizchg=true;
        paintGadget();
      }
      function endTransitionFast() {
        System.Gadget.endTransition(System.Gadget.TransitionType.morph, 0.1);
        fixCanvasBackground();
      }
    function readSettings() {
        background=System.Gadget.Settings.read("background");
        if(background==0) { background=2; System.Gadget.Settings.write("background",2); }
        theme=System.Gadget.Settings.read("theme");
        if(theme==0) { theme=1; System.Gadget.Settings.write("theme",1); }
        show_pc=System.Gadget.Settings.read("showpc");
        if(show_pc==0) { show_pc=1; System.Gadget.Settings.write("showpc",1); }
        show_net=System.Gadget.Settings.read("shownet");
        if(show_net==0) { show_net=1; System.Gadget.Settings.write("shownet",1); }
        local=System.Gadget.Settings.read("local");
        if(local==0) { local=2; System.Gadget.Settings.write("local",2); }
        media=System.Gadget.Settings.read("media");
        if(media==0) { media=2; System.Gadget.Settings.write("media",2); }
        network=System.Gadget.Settings.read("network");
        if(network==0) { network=2; System.Gadget.Settings.write("network",2); }
        remove=System.Gadget.Settings.read("remove");
        if(remove==0) { remove=2; System.Gadget.Settings.write("remove",2); }
      }
      function onLoad()
      {
        System.Gadget.settingsUI = "settings.html";
        System.Gadget.onSettingsClosed = onSettingsClosed;
        System.Gadget.onShowSettings = onShowSettings;
        readSettings();
        initDrives();
        timeout = window.setInterval(paintGadget, 2500);
        vizchg = true;
        paintGadget();
        return;
      }
    </script>
  </head>
  <body onload="onLoad()">
    <div id="targets"></div>
    <g:background id="canvas" src="images/canvas.png" style="position: absolute; top: 0; left: 0; width: 156; height: 200; z-index: -999;" opacity="0" />
  </body>
</html>

设置.html(设置窗口(:

<html>
  <head>
    <style>
      body { width: 250px; height: 800px; padding: 0px; margin: 0px; font-family: Tahoma; }
      body,p,div,span,td { font-size: 9pt; }
      label { font-weight: bold; }
      input,select { font: Arial; font-size: 9pt; }
      table { width: 100%; }
    </style>
    <script>
      var background, maxBackgrounds = 3, theme = 1, maxThemes = 7;
      function updateBackground()
      {
        var x = 84, y = 47, m;
        canvas.removeObjects();
        canvas.addImageObject('images/backgrounds/background' + background + '.png', x, y); 
        m = canvas.addImageObject('images/meterblue.png', x + 24, y + 34);   
        m.width = (0.25 * 128);
        m.left = x + 24 - ((128 - m.width) / 2);
        canvas.addImageObject('images/drives/drive3' + theme + '.png', x, y);
        canvas.addTextObject('Vista (C:)', 'Segoe UI', 11, 'white', x + 58, y + 5);
        canvas.addTextObject('40GB / 75%', 'Segoe UI', 10, 'white', x + 58, y + 17);
        //y -= 20;
        //canvas.addImageObject('images/backgrounds/background' + background + '.png', x, y); 
        //m = canvas.addImageObject('images/meterorange.png', x + 24, y + 34);  
        //m.width = (0.937 * 128);
        //m.left = x + 24 - ((128 - m.width) / 2);
        //canvas.addImageObject('images/drives/drive3.png', x, y);
        //canvas.addTextObject('Apps (D:)', 'Segoe UI', 11, 'white', x + 58, y + 5);
        //canvas.addTextObject('10GB / 6.3%', 'Segoe UI', 10, 'white', x + 58, y + 17);
        canvas.addImageObject('images/drives/drive3' + theme + '.png', x-85, y+130);
        canvas.addImageObject('images/drives/drive2' + theme + '.png', x-85, y+172);
        canvas.addImageObject('images/drives/drive4' + theme + '.png', x-85, y+215);
        canvas.addImageObject('images/drives/drive5' + theme + '.png', x-85, y+258);
      }
      function onBackground()
      {
        var e = window.event, o = e.srcElement, b = o.getAttribute('base');
        o.src = 'images/settings/' + b + (e.type == 'mouseover' || e.type == 'mouseup' ? 'hover': (e.type == 'mousedown' ? 'pressed': '')) + '.png';
        if (e.type == 'mouseup') 
        {
          if (b == 'next') background++; else background--;
          if (background < 1) background = maxBackgrounds;
          if (background > maxBackgrounds) background = 1;
          updateBackground();        
        }
      }
      function onTheme()
      {
        var e = window.event, o = e.srcElement, b = o.getAttribute('base');
        o.src = 'images/settings/' + b + (e.type == 'mouseover' || e.type == 'mouseup' ? 'hover': (e.type == 'mousedown' ? 'pressed': '')) + '.png';
        if (e.type == 'mouseup') 
        {
          if (b == 'next') theme++; else theme--;
          if (theme < 1) theme = maxThemes;
          if (theme > maxThemes) theme = 1;
          updateBackground();        
        }
      }

      function onClose(event)
      {
        if (event.closeAction == event.Action.commit) 
        {
          System.Gadget.Settings.write("background", background);
          System.Gadget.Settings.write("theme",      theme);
          System.Gadget.Settings.write("showpc",     document.boxes.mypc.checked ? 2 : 1);
          System.Gadget.Settings.write("shownet",    document.boxes.netw.checked ? 2 : 1);
          System.Gadget.Settings.write("remove",     document.boxes.remove.checked ? 2 : 1);
          System.Gadget.Settings.write("local",      document.boxes.local.checked ? 2 : 1);
          System.Gadget.Settings.write("network",    document.boxes.network.checked ? 2 : 1);
          System.Gadget.Settings.write("media",      document.boxes.media.checked ? 2 : 1);
        }
        event.cancel = false;
//      System.Gadget.beginTransition();
//      window.setTimeout(endtransit, 400); 
      }
/*    function endtransit() {
        System.Gadget.endTransition(System.Gadget.TransitionType.morph, 0.1);
      }*/

      function onLoad() 
      {
        var box;
        System.Gadget.onSettingsClosing = onClose;
        background = System.Gadget.Settings.read("background");
        if (background == 0) background = 2;
        theme = System.Gadget.Settings.read("theme");
        if (theme == 0) theme = 1;
        System.Gadget.Settings.read("remove")  == 2 ? document.boxes.remove.checked  = true : false;
        System.Gadget.Settings.read("local")   == 2 ? document.boxes.local.checked   = true : false;
        System.Gadget.Settings.read("network") == 2 ? document.boxes.network.checked = true : false;
        System.Gadget.Settings.read("media")   == 2 ? document.boxes.media.checked   = true : false;
        System.Gadget.Settings.read("showpc")  == 2 ? document.boxes.mypc.checked   = true : false;
        System.Gadget.Settings.read("shownet") == 2 ? document.boxes.netw.checked   = true : false;
        updateBackground();
      }
    </script>
  </head>
  <body onload="onLoad()">
    <g:background id="canvas" src="images/settings/desktop.png" style="position: absolute; left: 1; top: 1; z-index: -999;" />
    <div style="position: absolute; left: 0; top: 147px;">
      <table cellspacing="0" cellpadding="0">
        <tr>
          <td style="width: 33%; padding-right: 10px;" align="right"><img src="images/settings/previous.png" base="previous" style="cursor: hand;" onmouseover="onBackground();" onmouseout="onBackground();" onmousedown="onBackground();" onmouseup="onBackground();" /></td>
          <td style="width: 33%;" align="center"><label>Backgrounds</label></td>
          <td style="width: 33%; padding-left: 10px;" align="left"><img src="images/settings/next.png" base="next" style="cursor: hand;" onmouseover="onBackground();" onmouseout="onBackground();" onmousedown="onBackground();" onmouseup="onBackground();" /></td>
         </tr>
         <tr>
          <td style="width: 33%; padding-right: 10px;" align="right"><img src="images/settings/previous.png" base="previous" style="cursor: hand;" onmouseover="onTheme();" onmouseout="onTheme();" onmousedown="onTheme();" onmouseup="onTheme();" /></td>
          <td style="width: 33%;" align="center"><label>Icon Theme</label></td>
          <td style="width: 33%; padding-left: 10px;" align="left"><img src="images/settings/next.png" base="next" style="cursor: hand;" onmouseover="onTheme();" onmouseout="onTheme();" onmousedown="onTheme();" onmouseup="onTheme();" /></td>
        </tr>
      </table>
      <table cellspacing="0" cellpadding="0" style="margin-top: 15px;margin-left:60px;">
        <tr><td>
          <form name="boxes">
            <input type="checkbox" name="local">
                <font style="font-size: 8pt;">Local Drives</font><p>
            <input type="checkbox" name="remove">
                <font style="font-size: 8pt;">Removable Drives</font><p>
            <input type="checkbox" name="network">
                <font style="font-size: 8pt;">Network Drives</font><p>
            <input type="checkbox" name="media">
                <font style="font-size: 8pt;">Media Drives</font><p>
            <input type="checkbox" name="mypc">
                <font style="font-size: 8pt;">My Computer link</font><br>
            <input type="checkbox" name="netw">
                <font style="font-size: 8pt;">Network Link</font>
            </form>
        </td></tr>
      </table>
    </div>
  </body>
</html>

更新:

如果有帮助,这是完整的小工具来源:

https://www.mediafire.com/?c8h1271714sp6tz

系统上安装的驱动程序之一总是有可能泄漏导致此问题。但是,在查看该 JavaScript 代码时,有一种模式在过去引起了问题,现在有了解决方案。

小工具的主循环如下所示:

function paintGadget() {
    // repaint/rebuild all UI elelments
    // remove all elements
    targets.innerHtml = '';
    // buildup
     var o = document.createElement('DIV');
     o.onclick = openDrive;
     targets.appendChild(o);
}
function openDrive() {
}
window.setInterval(paintGadget, 2500);

这基本上意味着:每2.5秒调用一次paintGadget,永远

如果 javascript 引擎及其资源不再在任何范围内时被垃圾回收,这应该没问题。在这里,由于草率的编程,事情可能会变得糟糕。

根据用户 dsg 的回答,我们了解到事件侦听器是垃圾回收失败的根本原因。

为了克服这个问题,我们必须将函数paintGadget中的行targets.innerHtml = '';替换为一个实现,该实现在删除元素本身之前删除每个元素上的事件处理程序,如下所示:

while(targets.firstChild) {
    var ch = targets.firstChild;
    ch.onclick = null;
    targets.removeChild(ch);              
}

如介绍中所述,paintGadget 在画布上做得更多,它遵循类似的模式,删除所有内容并重新创建。如果那里有泄漏,也需要重新实现。