Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

missing Close() method for VirtualNetwork #161

Open
urish opened this issue Nov 22, 2022 · 9 comments
Open

missing Close() method for VirtualNetwork #161

urish opened this issue Nov 22, 2022 · 9 comments
Assignees

Comments

@urish
Copy link

urish commented Nov 22, 2022

Currently, there's no way to free the memory allocated by VirtualNetwork.

For instance, the following code will leak around ~100MB of memory:

	config := types.Configuration{
		Debug:             false,
		CaptureFile:       "",
		MTU:               1500,
		Subnet:            "10.10.10.0/16",
		GatewayIP:         "10.10.10.1",
		GatewayMacAddress: "12:34:56:78:aa:bb",
		DHCPStaticLeases:  map[string]string{},
		DNS:               []types.Zone{},
		Forwards:          map[string]string{},
		NAT:               map[string]string{},
		GatewayVirtualIPs: []string{"10.10.10.254"},
		Protocol:          types.QemuProtocol,
	}

	fmt.Println("Memory usage before creating virtual networks:")
	memStats()

	for i := 0; i < 1000; i++ {
		_, err := virtualnetwork.New(&config)
		if err != nil {
			panic(err)
		}
	}

	runtime.GC()

	fmt.Println("Memory usage after creating virtual networks:")
	memStats()

Output:

Memory usage before creating virtual networks:
Alloc = 0 MiB   TotalAlloc = 0 MiB      Sys = 10 MiB    NumGC = 0
Memory usage after creating virtual networks:
Alloc = 97 MiB  TotalAlloc = 125 MiB    Sys = 410 MiB   NumGC = 6

The implementation of memStats() (taken from here):

func memStats() {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	// For info on each, see: https://golang.org/pkg/runtime/#MemStats
	fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
	fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
	fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
	fmt.Printf("\tNumGC = %v\n", m.NumGC)

}

func bToMb(b uint64) uint64 {
	return b / 1024 / 1024
}
@urish
Copy link
Author

urish commented Nov 23, 2022

Created a small GitHub repo with a reproduction:

https://github.com/urish/virtualnetwork-leak

It's basically the same code as above, but packaged into a go project for easier reproduction.

@gbraad gbraad self-assigned this Nov 30, 2022
@gbraad
Copy link
Member

gbraad commented Nov 30, 2022

Thanks. Sorry for not noticing this earlier, but I will take a look at this.

@urish
Copy link
Author

urish commented Nov 30, 2022

Thanks!

@gbraad
Copy link
Member

gbraad commented Dec 2, 2022

The code as in the example

	for i := 0; i < 1000; i++ {
		_, err := virtualnetwork.New(&config)
		if err != nil {
			panic(err)
		}
	}

	runtime.GC()

just claims a new device and never release, hence an out-of-memory.
The queston however is when you want to release it. Opening multiple is allowed and is hold on until released/closed. At what point would you make that decision?

@urish
Copy link
Author

urish commented Dec 2, 2022

From my point of view, having a Close() method which would release a VirtualNetwork would be ideal

@gbraad
Copy link
Member

gbraad commented Dec 2, 2022 via email

@urish
Copy link
Author

urish commented Dec 2, 2022

Yes 😀

@cfergeau
Copy link
Contributor

cfergeau commented Dec 6, 2022

Spent some time on this, and most of the memory consumption is coming from VirtualNetwork.stack which has a Close() method. However, adding a VirtualNetwork.Close() with just a call to n.stack.Close() is not enough as this then results in dozens of ERRO[0000] EOF being logged. This has to do with what is done in pkg/virtualnetwork.addServices() as commenting out the call in pkg/virtualnetwork.New() removes the logs, but I stopped the investigation there for now, easy to get lost in that code :-/

@gbraad
Copy link
Member

gbraad commented May 26, 2023

will also have to spend more time on this. I see memory leaks on Windows, but they are caused by something else that fails first

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants