nedpals / vex

Easy-to-use, modular web framework built for V
https://nedpals.github.io/vex
MIT License
340 stars 28 forks source link

Critical memory leaks #53

Closed xD0135 closed 2 years ago

xD0135 commented 2 years ago

Thanks for creating this project. While performing a simple test to verify the speed and resource consumption of your framework I discovered critical memory leaks that would prevent anyone from using this framework in a production environment. Details are below.

System info:

OS: Manjaro Linux x86_64
Kernel: 5.15.28-1-MANJARO
CPU: Intel i9-9900K (16) @ 5.000GHz
Memory: 64227MiB

Simple server:

# vex-server.v

module main

import nedpals.vex.router
import nedpals.vex.server
import nedpals.vex.ctx

fn main() {
    mut app := router.new()
    app.route(.get, '/', fn (req &ctx.Req, mut res ctx.Resp){
        res.send('', 200)
    })
    server.serve(app, 6789)
}

Compile and start the server:

$ v vex-server.v
$ ./vex-server
[VEX] HTTP Server has started.
[VEX] Running On http://localhost:6789

Take the initial memory allocation:

$ pmap $(pgrep vex-server) | grep total
 total             6048K

Execute a simple load test (I'm using pewpew here)

$ pewpew benchmark http://127.0.0.1:6789 --rps 200 -d 30

Take the memory allocation after the load test:

$ pmap $(pgrep vex-server) | grep total
 total           835404K

You can see the memory spiked from ~8MB to ~835MB in just 30 seconds of running the load test with ~200 requests/sec.

Furthermore, repeating the above tests with Valgrind indeed confirms the leaks. First start the server with valgrind then run the benchmark with pewpew once again:

$ valgrind ./vex-server 
==1907295== Memcheck, a memory error detector
==1907295== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1907295== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==1907295== Command: ./vex-server
==1907295== 
[VEX] HTTP Server has started.
[VEX] Running On http://localhost:6789
^C==1907295== 
==1907295== Process terminating with default action of signal 2 (SIGINT)
==1907295== 
==1907295== HEAP SUMMARY:
==1907295==     in use at exit: 808,412,463 bytes in 801,440 blocks
==1907295==   total heap usage: 957,621 allocs, 156,181 frees, 827,025,445 bytes allocated
==1907295== 
==1907295== LEAK SUMMARY:
==1907295==    definitely lost: 19,422,529 bytes in 654,341 blocks
==1907295==    indirectly lost: 787,359,215 bytes in 144,001 blocks
==1907295==      possibly lost: 1,310,720 bytes in 10 blocks
==1907295==    still reachable: 319,999 bytes in 3,088 blocks
==1907295==         suppressed: 0 bytes in 0 blocks
==1907295== Rerun with --leak-check=full to see details of leaked memory
==1907295== 
==1907295== For lists of detected and suppressed errors, rerun with: -s
==1907295== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

With Valgrind, what you want to see to ensure no memory leaks are found is the following output:

HEAP SUMMARY:
    in use at exit: 0 bytes in 0 blocks

All heap blocks were freed -- no leaks are possible

ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
benstigsen commented 2 years ago

This is not specific to Vex, but with the V programming language. As -autofree becomes more stable, less leaks should happen. For now you can compile with -gc boehm, which will take a minor performance hit, but it'll eliminate leaks. Beware, garbage collectors and Valgrind don't go well together though.

xD0135 commented 2 years ago

Reading through V's documentation on Memory management we see the following:

Note: right now autofree is hidden behind the -autofree flag. It will be enabled by default in V 0.3. If autofree is not used, V programs will leak memory.

Note 2: Autofree is still WIP. Until it stabilises and becomes the default, please compile your long running processes with -gc boehm, which will use the Boehm-Demers-Weiser conservative garbage collector, to free the memory, that your programs leak, at runtime.

Compiling the program with the recommended options:

$ v -gc boehm -o vex-server server.v

Take the initial memory allocation:

$ pmap $(pgrep vex-server) | grep total
 total           130576K

Run the benchmark with pewpew again and look at the memory allocation after the load test:

$ pmap $(pgrep vex-server) | grep total
 total           130576K

We can see this is no longer an issue. Thanks!