To me the semantics don't matter. A program is leaky if it starts using up a boatload of memory and running slower and slower the longer you run it, like a video game which requires you to restart every 30 minutes because the frame rates keep dropping the longer you play it while it goes from taking megabytes to gigabytes of memory. Most games that exhibit these leaky symptoms use garbage collection and for a good reason: garbage collection tends to exchange immediately reproducible crashes for leaks that fly under the radar.
In that sense above, it can actually be easier to introduce leaks into a program built using a language with GC, since all you have to do to root a resource and prevent it from being freed is store a reference to it.
As a simple example, let's say you have a video game which has a physics system to move particles around and a renderer to render a list of particles it references as well. When a particle dies, it fades off the screen and stops being visible, at which point the physics system removes it from the list of particles to process.
Voila, now you have a leak because you didn't remove the particle reference from the renderer. However, it won't be obvious in game because the dead particles have an opacity of zero. Nevertheless, the game would be creating a bigger and bigger list of particles that are never freed until the game is shut down, and spending more and more time in increasingly larger loops in the renderer as the particle list grows larger and larger. This might fly under the radar of the developers indefinitely to the point where they actually suggest to users to restart the game from time to time if it gets slow while bumping up the system requirements to beefy machines even for a simple 2D game.
Meanwhile in C, this would have simply lead to an immediately detectable crash since the physics system would have manually destroyed the particle when it died. The renderer would then try to access a particle which was destroyed and most likely come crashing down during the first play test which is arguably more preferable in this case than having game-halting leaks which go unsolved indefinitely.
A very firm way to avoid this problem is to rely on concepts like weak and phantom references and decide who actually manages the particles. If it's the physics system, then the renderer should keep weak references to particles so that it doesn't prolong their lifespan and can detect when they are destroyed and hopefully even run into a game-halting error if the renderer tries to access a particle that no longer exists.
In general GC doesn't protect you against having to think about resource management and who owns a resource and having to manually free resources (assigning them as
none/nil/null) to avoid logical leaks. Its primary usefulness in my opinion is in the context of areas like multithreading where you want to ensure that an object is not destroyed until a thread is finished processing the object.
The ideal solution to me if a language could ever provide it at the native language level (C++ is the closest I can think with
shared_ptr, but it's a library concept and can't detect cycles since it uses basic ref counting) is one that could let you opt into garbage collection per object. For example, perhaps the object is normally destroyed when it either goes out of scope or is assigned a null, and everything that references it outside of its immediate scope effectively acts as a pointer to it, not preventing the object from being destroyed. However, something that wants to share ownership of that object, like a thread, could call a
ref method to increase its reference count and
deref to decrement it to prevent it from being destroyed until the thread is finished using it, or it could use some kind of shared reference concept that avoids the need for an explicit
deref in the thread's scope, with a "pay as you use it" cost.