r/PowerShell • • 7d ago

Question Methods of a class that's not instanced

Hi there,

i can't seem to find relevant informations for this particular topic using my own google skills so maybe someone here can shed some light on this.

if i use a class that doesen't provide any methods without creating an instance of it, but i skip creating an instance via "new().SomeMethod", will this leak memory?

example, sending an async ping to some host without creating a $Ping Object first:

[System.Net.NetworkInformation.Ping]::new().SendPingAsync($IPAddress)

Usually one would create a $Ping object, ping something and in the end, dispose of the $Ping to free the ressources.

If the SendPingAsync Method could work without an instance of the class, the class probably would have some static method's providing the mechanism. So an instance is probably created somewhere but that instance is probably not automatically disposed after the fact?

Can this method be used without worries or should such trickery be avoided?

thanks in advance

3 Upvotes

11 comments sorted by

9

u/AdeelAutomates 7d ago edited 7d ago

::new() is the constructor on the class. It creates an instance of Ping (the object).

So.SendPingAsync($IPAddress) is then an instance method being called on that newly created object.

So this: [System.Net.NetworkInformation.Ping]::new().SendPingAsync($IPAddress)

is effectively just a shortened version of:

$ping = [System.Net.NetworkInformation.Ping]::new()
$ping.SendPingAsync($IPAddress)

Just without the variable being set.

So the shorthand works, but it does not somehow avoid creating an instance... it just creates a temporary one as part of the cmd and immediately calls the method to output it.

1

u/xXFl1ppyXx 7d ago edited 7d ago

well, yeah but by that reasoning why dispose anything because everything is ultimately only temporary and lives for as long as the pwsh process runs

i know that i can do

[System.Net.NetworkInformation.Ping]::new().SendPingAsync($IPAddress)

instead of

$ping = [System.Net.NetworkInformation.Ping]::new()
$ping.SendPingAsync($IPAddress

but my question was if that is advisable because with the first method this:

$ping.Dispose()

is missing. and i probably won't need to try to do this:

[System.Net.NetworkInformation.PingReply]$PingReply.Dispose()

€dit:

Ping is just an example, probably without much impact but there are other examples that could be more impactful

[System.Net.Sockets.TcpClient]::new($IPAddress, $Port).ConnectAsync()

[System.Net.Sockets.TcpClient]::new($IPAddress, $Port).GetStream()

2

u/ka-splam 7d ago edited 7d ago

well, yeah but by that reasoning why dispose anything because everything is ultimately only temporary and lives for as long as the pwsh process runs

This, but unironically. You're in a scripting language, on top of the managed runtime .NET framework, with a garbage collector. Let it do stuff.

As far as I know .Dispose() doesn't get you any memory back anyway(!) until until the Garbage Collector runs later on, and even then the Garbage Collector goes though the memory allocated to PowerShell and marks some of it free for PowerShell to reuse - it doesn't give it back to the OS so you won't even see that the memory is freed. (Maybe if it's a ton of memory, or there is memory pressure on the OS).

.Dispose() is more so that you (C# devs) can unwind internal state cleanly - like making sure to write the end of an XML stream to a file before it gets closed, or writing a "finished" message to a logfile, to close open files and close TCP ports so other processes can have them - and not primarily for releasing memory.

But PowerShell scripts are usually short-running (minutes and hours, not weeks and months), variables fall out of scope and the Garbage Collector will get them after that, and if you are doing something with a lot of memory use that's either the main point of your script, or it's a big mistake in the design (get-childitem c:\ -recurse) and worrying about a few KB of ping object is rearranging the deckchairs while the Titanic tries to load an ocean of water into its hold.

https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose - "The Dispose method is primarily implemented to release unmanaged resources"

1

u/CookinTendies5864 7d ago

I believe it’s best practice to avoid relying on the garbage collector(GC). Even though you’re right sooner or later it will clean up whatever resources are still open.

The nuance is when you have multiple resources open for different ip addresses. For a single instance it is minimal whether we implicitly dispose or explicitly dispose.

0

u/Thotaz 7d ago

Ideally you always run Dispose() if your object implements that method. However, if you don't do that then it's usually not a big deal because those objects will also usually implement a custom finalizer that makes sure to do the proper disposing when the garbage collector sees that there are no references to the object anymore.

In the shell, I wouldn't worry about it. In a script, I'd put Dispose in a finally block, like so:

$Ping = [System.Net.NetworkInformation.Ping]::new()
try
{
    $Ping.SendAsync()
}
finally
{
    $Ping.Dispose()
}
→ More replies (3)

3

u/CeleryMan20 7d ago edited 7d ago

This covers .Net CLR, not PowerShell-specific, but it says that object method code is loaded into the heap:

~https://thinkobjectoriented.hashnode.dev/understanding-static-attributes-and-methods~

I don’t have details on how the PS runspace relates to the CLR, though.

Note that, for OOP generally, if you create 100 instances of a class, you don’t create 100 copies of the class’ methods, regardless of whether they are declared static. The whole point is that instances are stamped from the same cookie cutter and share the same methods.

ETA: The page at that link is a bit flakey for me. Here is Eric Lippert on heap versus stack in .Net CLR. Essentially, method code and much instance data both live on the heap, but it’s implementation-specific. https://learn.microsoft.com/en-au/archive/blogs/ericlippert/the-truth-about-value-types