It appears you should refine your knowledge of how contemporary computer systems work—this may help working with languages such as Go, which are reasonably "closer to the metal" than some other mainstream solutions.
The thing is, the commodity platforms current Go runs on (except maybe WebAssembly—which runs on a high-level VM typically provided by a web browser) do two things related to the problem in question: fully "contain" the processes they're executing, and provide to them the so-called
virtual memory management.
The former means a process running by the kernel of a commodity
OS (Linux- or *BSD-based, Windows, Mac OS X etc) has no way to
do anything useful without asking the underlying kernel to do that
on behalf of that process. That is, the process does not actually allocate a memory or open a file or send bytes over a TCP socket—it asks the kernel to do so.
As a result, the kernel has full and exact account of all the resources allocated to each of the processes it manages.
Consequently, when a process finishes executing for some reason,
the kernel cleans up after it: closes file and socket descriptors
which are still open, deallocates other resources and reclaims
the memory which was committed to the process.
So the direct answer to your question is: of course, yes,
and this does not in any way depend on how a process was
implemented (that is, has it been written in C or in Go
or in Java—and hence was run by the Java VM and compiled on
the fly from the byte codes to real CPU instructions, or in JavaScript and executed by Node.js, or in Python—and hence the
kernel wasn't actually executing your Python code but actually
the Python interpreter's code, and so on and so on)—the kernel
does not even "see" all these differences as it executes
CPU instructions and performs
the syscalls
made by the processes (again, using CPU instructions).
The remaining part, which is only tangentially related to
the essence of your question, but anyway may be helpful to
think about is that of memory management.
The thing is, if we consider a process executing a program
written in Go which is running on one of operating systems
Go supports, there exist two levels of memory management:
- The low-level, provided by the kernel, and
- The high-level, provided by the Go runtime (which is
linked to any running program written in Go).
The Go program asks the Go runtime which powers it to allocate
memory, when needed, and the garbage collector, which again
is a part of the Go runtime, from time to time reclaims
the memory which got unused back to the memory pool maintained
by the runtime. In essence, the memory which gets "freed"
while a Go program is running stays within the program,
and when it is needed again, the Go runtime will try to
allocate it from its pool.
Only when the Go runtime's memory manager has not enough free
memory, will it reach for the OS and allocate memory there—thus
engaging that another layer of memory management.
Surely, the memory does not only flow from the kernel to the
Go runtime: the latter has a mechanism to mark memory pages
which stay free for too long as unused so that the kernel is
free to reclaim them back when it sees fit (typically when
the system gets under a memory pressure).
I'd say, you should read a book or two on how a typical operating
system works.