Seeming GDI Objects leak in v28

Things you’d like to miss in the future...
Forum rules
:warnred20: :warnred20: :warnred20: :warnred20: :warnred20: READ THIS AND DO IT!!! :warnred20: :warnred20: :warnred20: :warnred20: :warnred20:

:info: Please include the following information:
1) Your XYplorer Version (e.g., v28.00.0801)
2) Your Windows Version (e.g., Win 11)
3) Your Screen Scaling Percentage (e.g., 125%).

:info: We recommend adding your Windows Version and Screen Scaling Percentage to the Location field in your Profile or to your Signature. That way, you only have to type them once.

:info: When attaching an Image, please use the Attachment tab at the bottom of your post and click "Add files".

:warnred20: :warnred20: :warnred20: :warnred20: :warnred20: READ THIS AND DO IT!!! :warnred20: :warnred20: :warnred20: :warnred20: :warnred20:
Post Reply
rseiler
Posts: 28
Joined: 25 Jun 2017 23:22
Location: Win11 25H2, 100% scaling

Seeming GDI Objects leak in v28

Post by rseiler »

I'm currently on 28.10.0200, and I think I've been on each of the v28 versions since they've come out (even a few from the late beta period back then). I mention this because I was also on v27 (x86) in its day and I never saw the issue I'm about to describe with that version on the same Win11 system. This seems to be something about v28, and I've seen it happen with numerous versions in this line (some versions were replaced so quickly that the problem never had a chance to occur)

I normally keep the Win11 25H2 session going for at least a week at a time without rebooting. In that time, XY is never closed. This allows GDI Objects (visible in Task Manager, Details; or Process Explorer) to grow over time. It starts for XY at about 1289, which is unusual in itself (no other program is even close, and I have a lot of them), but if it stayed there or at least stayed under 10K (the hard limit, unless you edit the Registry), there wouldn't be a problem. v28 does not, however, at least for me. GDI grows for XY at a pace of about 500 every 4 hours (rate of increase may vary by usage--average use yields about this figure for me).

When it gets to 10K, you can't use it anymore. It's very hard to describe, but when you click it to use it (from its minimized state), you see a kind of fractured XY window, which makes sense given what GDI Objects are. It's not crashed, since you can close it, but this is classically what happens (with any program) in a GDI leak hitting the ceiling.

Since GDI Objects are the very definition of obscure, I have a Powershell script that shows the top processes for not only GDI but also User Obj, Threads, and Handles. This is the first thing I run when something is unexplained. To be clear, XY has NO problem with the other three, but I like to include them since they're known troublemakers in general (especially Handles).

Code: Select all

if(-not ('Win32' -as [type])){Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public static class Win32 { [DllImport("user32.dll")] public static extern int GetGuiResources(IntPtr hProcess,int uiFlags); }'}; $esc=[char]27; $red=if(Test-Path variable:PSStyle){$PSStyle.Foreground.Red}else{"$esc[31m"}; $grn=if(Test-Path variable:PSStyle){$PSStyle.Foreground.Green}else{"$esc[32m"}; $dim=if(Test-Path variable:PSStyle){$PSStyle.Foreground.BrightBlack}else{"$esc[90m"}; $rst=if(Test-Path variable:PSStyle){$PSStyle.Reset}else{"$esc[0m"}; $b=if(Test-Path variable:PSStyle){$PSStyle.Bold}else{"$esc[1m"}; $boff=if(Test-Path variable:PSStyle){$PSStyle.BoldOff}else{"$esc[22m"}; $fmt={param([double]$x) if($x -ge 1GB){"{0:N1}GB" -f ($x/1GB)} elseif($x -ge 1MB){"{0:N0}MB" -f ($x/1MB)} elseif($x -ge 1KB){"{0:N0}KB" -f ($x/1KB)} else{"{0:N0}B" -f $x}}; $p=Get-Process|ForEach-Object{ $g=$null;$u=$null;$n=$null; if($_.Id -eq 4){$n='System (ntoskrnl.exe)'} elseif($_.Id -eq 0){$n='System Idle Process'} else { try{$n=$_.MainModule.ModuleName}catch{}; if(-not $n){$n="$($_.ProcessName).exe"} }; try{$g=[Win32]::GetGuiResources($_.Handle,0)}catch{}; try{$u=[Win32]::GetGuiResources($_.Handle,1)}catch{}; [pscustomobject]@{Process=$n;Id=$_.Id;Handles=$_.HandleCount;Threads=$_.Threads.Count;GDI=$g;USER=$u} }; $rows=@($p|Sort Handles -Desc|Select -First 3|%{[pscustomobject]@{Order=1;Metric='Handles';Process=$_.Process;Count=[int]$_.Handles;Id=[int]$_.Id}})+@($p|Sort Threads -Desc|Select -First 3|%{[pscustomobject]@{Order=2;Metric='Threads';Process=$_.Process;Count=[int]$_.Threads;Id=[int]$_.Id}})+@($p|?{$_.GDI -ne $null}|Sort GDI -Desc|Select -First 3|%{[pscustomobject]@{Order=3;Metric='GDI Obj';Process=$_.Process;Count=[int]$_.GDI;Id=[int]$_.Id}})+@($p|?{$_.USER -ne $null}|Sort USER -Desc|Select -First 3|%{[pscustomobject]@{Order=4;Metric='User Obj';Process=$_.Process;Count=[int]$_.USER;Id=[int]$_.Id}}); $mw=(@('Metric')+($rows.Metric|%{$_.ToString()})|Measure-Object Length -Maximum).Maximum; $pw=(@('Process')+($rows.Process|%{$_.ToString()})|Measure-Object Length -Maximum).Maximum; $cw=(@('Count')+($rows.Count|%{$_.ToString()})|Measure-Object Length -Maximum).Maximum; $iw=(@('Id')+($rows.Id|%{$_.ToString()})|Measure-Object Length -Maximum).Maximum; $hdr=("Metric".PadRight($mw)+"  "+"Process".PadRight($pw)+"  "+"Count".PadLeft($cw)+"   "+"Id".PadLeft($iw)); $sep='-'*$hdr.Length; $out=New-Object System.Collections.Generic.List[string]; $out.Add($hdr); $out.Add($sep); $prev=$null; foreach($r in ($rows|Sort Order,@{Expression='Count';Descending=$true})){ if($prev -and $r.Order -ne $prev){$out.Add($sep)}; $prev=$r.Order; $thr=1000; switch($r.Metric){'Handles'{$thr=10000}'Threads'{$thr=500}'GDI Obj'{$thr=5000}'User Obj'{$thr=5000}}; $cnt=$r.Count.ToString().PadLeft($cw); $cntColor=$grn; if($r.Count -ge $thr){$cntColor=$red}; $cntc=$cntColor+$cnt+$rst; $idc=$dim+($r.Id.ToString().PadLeft($iw))+$rst; $out.Add($r.Metric.PadRight($mw)+"  "+$r.Process.PadRight($pw)+"  "+$cntc+"   "+$idc) }; $svchostPids=@($rows | Where-Object{$_.Process -ieq 'svchost.exe'} | Select-Object -ExpandProperty Id -Unique); if($svchostPids.Count -gt 0){ $out.Add(''); $out.Add('svchost.exe contents (top list only):'); foreach($spid in $svchostPids){ $svcs=Get-CimInstance Win32_Service -Filter ("ProcessId={0}" -f $spid) -ErrorAction SilentlyContinue | Sort-Object Name | Select-Object Name,DisplayName; if($svcs){ $out.Add(("  PID {0}: {1}" -f $spid,(($svcs|ForEach-Object{ "{0} ({1})" -f $_.Name,$_.DisplayName }) -join ', '))) } else { $out.Add(("  PID {0}: <none found>" -f $spid)) } } }; $cs=(Get-Counter '\Memory\Available Bytes','\Memory\Committed Bytes','\Memory\Commit Limit','\Memory\Cache Bytes','\Memory\Pool Paged Bytes','\Memory\Pool Nonpaged Bytes').CounterSamples; $cv={param([string]$suffix) [double]($cs|Where-Object{$_.Path -like "*$suffix"}|Select-Object -First 1 -ExpandProperty CookedValue)}; $tp=[double](Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory; $avail=& $cv '\Memory\Available Bytes'; $inuse=$tp-$avail; $comm=& $cv '\Memory\Committed Bytes'; $clim=& $cv '\Memory\Commit Limit'; $cache=& $cv '\Memory\Cache Bytes'; $pp=& $cv '\Memory\Pool Paged Bytes'; $np=& $cv '\Memory\Pool Nonpaged Bytes'; $out.Add(''); $out.Add(("Memory: "+$b+"InUse"+$boff+" $(& $fmt $inuse)  "+$b+"Avail"+$boff+" $(& $fmt $avail)")); $out.Add(("        "+$b+"Commit"+$boff+" $(& $fmt $comm)/$(& $fmt $clim)  "+$b+"Cache"+$boff+" $(& $fmt $cache)")); $out.Add(("        "+$b+"PagedPool"+$boff+" $(& $fmt $pp)  "+$b+"NonPagedPool"+$boff+" $(& $fmt $np)")); ([Environment]::NewLine + ($out -join [Environment]::NewLine) + [Environment]::NewLine)

admin
Site Admin
Posts: 65245
Joined: 22 May 2004 16:48
Location: Win8.1, Win10, Win11, all @100%
Contact:

Re: Seeming GDI Objects leak in v28

Post by admin »

Confirmed! This is clearly a tB bug, probably a couple of them (the language XY64 is written in is so new, it's still in beta). I'll watch it, isolate it, report it, and wait for it. It will take time but eventually it will be fixed. Thanks for the report! :tup:

JohnM
Posts: 286
Joined: 08 Jun 2006 11:59
Location: Windows 11 Pro @100%

Re: Seeming GDI Objects leak in v28

Post by JohnM »

Interesting...

Since I started watching it, my GDI Object count has moved from about 2500 to 3037.

Is it possible that this GDI Object leak is cause of my hangs? (see: viewtopic.php?t=29106)
Windows 11 Pro x64
XYplorer 28.10.0203 x64

rseiler
Posts: 28
Joined: 25 Jun 2017 23:22
Location: Win11 25H2, 100% scaling

Re: Seeming GDI Objects leak in v28

Post by rseiler »

@admin. That's good to hear. As a test, I tried some of the things I usually do, this time while watching GDI live (in Task Manager, off to the side), but aside from opening Preferences, which takes a whopping 1K GDI (crucially, these are decremented once closing Preferences), I only saw small amounts. I don't do anything fancy at all: copies, moves, moving around directories a lot, hovering over files to get the popup info, accessing a few toolbar items like Mini Tree, hovering over the file icon to get picture or video previews. I thought those last two were bound to be it, but I don't think so.

But doing one of those, or some combination, starts the ball rolling, and at that point it becomes inevitable given enough time. The ascent can accelerate unexpectedly somehow (otherwise, I would have had to stay up all night clicking away in XY to get to 6.8K in the last 24 hours).

@JohnM: It doesn't sound like it given that you also mentioned the x86 version. Beyond that, I've never seen it hang, just become unusable because it no longer looks like itself--it can still be closed by right-clicking the taskbar icon.

JohnM
Posts: 286
Joined: 08 Jun 2006 11:59
Location: Windows 11 Pro @100%

Re: Seeming GDI Objects leak in v28

Post by JohnM »

@JohnM: It doesn't sound like it given that you also mentioned the x86 version.
Sorry for the misunderstanding, for me it is only x64. The OP mentioned both x86 and x64. I just hijacked the topic. :whistle:
Windows 11 Pro x64
XYplorer 28.10.0203 x64

rseiler
Posts: 28
Joined: 25 Jun 2017 23:22
Location: Win11 25H2, 100% scaling

Re: Seeming GDI Objects leak in v28

Post by rseiler »

Oh, I didn't even notice that you weren't the top post.

It's certainly possible then. Maybe this manifests a little differently. You'll know for sure by checking GDI the next time it happens. If it's not sitting on 9999, it's a different issue.

Speaking of which, what is the "tB issue" mentioned at the end? tB=toolbar?

JohnM
Posts: 286
Joined: 08 Jun 2006 11:59
Location: Windows 11 Pro @100%

Re: Seeming GDI Objects leak in v28

Post by JohnM »

I will check my GDI Count next time. Thanks.

"tB" is TwinBASIC (see: https://twinbasic.com/), which is the language x64 XY is built in. For some discussion see: viewtopic.php?t=28273
Windows 11 Pro x64
XYplorer 28.10.0203 x64

Post Reply