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

Memory leak after moka::sync::Cache is dropped #176

Closed
losfair opened this issue Sep 1, 2022 · 4 comments · Fixed by #177
Closed

Memory leak after moka::sync::Cache is dropped #176

losfair opened this issue Sep 1, 2022 · 4 comments · Fixed by #177
Assignees
Labels
bug Something isn't working
Milestone

Comments

@losfair
Copy link

losfair commented Sep 1, 2022

After a sync::Cache is dropped, it seems like some memory is not freed.

Reproduce:

use moka::sync::Cache;

fn main() {
    let cache: Cache<u32, u32> = Cache::new(1000);
    for i in 0..1000u32 {
        cache.insert(i, i);
    }
    drop(cache);
}

and run it with Valgrind:

$ valgrind --leak-check=full ./target/release/moka-memleak
# ...
==910805== 80,000 (48,000 direct, 32,000 indirect) bytes in 1,000 blocks are definitely lost in loss record 29 of 29
==910805==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==910805==    by 0x118F5D: moka::sync_base::base_cache::BaseCache<K,V,S>::do_insert_with_hash::{{closure}} (in /home/ubuntu/Projects/moka-memleak/target/release/moka-memleak)
==910805==    by 0x118C52: moka::cht::map::bucket::InsertOrModifyState<K,V,F>::into_insert_bucket (in /home/ubuntu/Projects/moka-memleak/target/release/moka-memleak)
==910805==    by 0x116087: moka::cht::map::bucket::BucketArray<K,V>::insert_or_modify (in /home/ubuntu/Projects/moka-memleak/target/release/moka-memleak)
==910805==    by 0x12CC7E: moka::cht::map::bucket_array_ref::BucketArrayRef<K,V,S>::insert_with_or_modify_entry_and (in /home/ubuntu/Projects/moka-memleak/target/release/moka-memleak)
==910805==    by 0x12969B: moka_memleak::main (in /home/ubuntu/Projects/moka-memleak/target/release/moka-memleak)
==910805==    by 0x1362A2: std::sys_common::backtrace::__rust_begin_short_backtrace (in /home/ubuntu/Projects/moka-memleak/target/release/moka-memleak)
==910805==    by 0x136DB8: _ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17hcd59787f08a77695E.llvm.15037216701979191285 (in /home/ubuntu/Projects/moka-memleak/target/release/moka-memleak)
==910805==    by 0x1568C5: call_once<(), (dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (function.rs:280)
==910805==    by 0x1568C5: do_call<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panicking.rs:492)
==910805==    by 0x1568C5: try<i32, &(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe)> (panicking.rs:456)
==910805==    by 0x1568C5: catch_unwind<&(dyn core::ops::function::Fn<(), Output=i32> + core::marker::Sync + core::panic::unwind_safe::RefUnwindSafe), i32> (panic.rs:137)
==910805==    by 0x1568C5: {closure#2} (rt.rs:128)
==910805==    by 0x1568C5: do_call<std::rt::lang_start_internal::{closure_env#2}, isize> (panicking.rs:492)
==910805==    by 0x1568C5: try<isize, std::rt::lang_start_internal::{closure_env#2}> (panicking.rs:456)
==910805==    by 0x1568C5: catch_unwind<std::rt::lang_start_internal::{closure_env#2}, isize> (panic.rs:137)
==910805==    by 0x1568C5: std::rt::lang_start_internal (rt.rs:128)
==910805==    by 0x12A741: main (in /home/ubuntu/Projects/moka-memleak/target/release/moka-memleak)
==910805== 
==910805== LEAK SUMMARY:
==910805==    definitely lost: 48,000 bytes in 1,000 blocks
==910805==    indirectly lost: 32,000 bytes in 1,000 blocks
==910805==      possibly lost: 2,388 bytes in 9 blocks
==910805==    still reachable: 14,432 bytes in 73 blocks
==910805==         suppressed: 0 bytes in 0 blocks
==910805== Reachable blocks (those to which a pointer was found) are not shown.
==910805== To see them, rerun with: --leak-check=full --show-leak-kinds=all
@tatsuya6502
Copy link
Member

tatsuya6502 commented Sep 2, 2022

Thank you so much for reporting the issue. I verified the memory leak at drop. I will definitely fix the last one below, which should be a bug. Others might be expected but I will check to see if they can be mitigated.

No memory lost

// [dependencies]
// moka = "=0.9.3"
// crossbeam-epoch = "=0.9.10"

fn main() {
    let cache = moka::sync::Cache::builder()
        .max_capacity(1000)
        .thread_pool_enabled(false)  // This is important.
        .build();

    for i in 0..1000u32 {
        cache.insert(i, i);
    }

    // This is also important.
    for i in 0..1000u32 {
        cache.invalidate(&i);
    }

    drop(cache);

    // std::thread::sleep(std::time::Duration::from_secs(1));
    // crossbeam_epoch::pin().flush();
}
==16840== LEAK SUMMARY:
==16840==    definitely lost: 0 bytes in 0 blocks
==16840==    indirectly lost: 0 bytes in 0 blocks
==16840==      possibly lost: 0 bytes in 0 blocks
==16840==    still reachable: 13,104 bytes in 7 blocks
==16840==         suppressed: 0 bytes in 0 blocks

76 bytes lost

This is expected.

fn main() {
    let cache = moka::sync::Cache::builder()
        .max_capacity(1000)
        // .thread_pool_enabled(false)
        .build();

    for i in 0..1000u32 {
        cache.insert(i, i);
    }

    for i in 0..1000u32 {
        cache.invalidate(&i);
    }

    drop(cache);

    std::thread::sleep(std::time::Duration::from_secs(1));
    crossbeam_epoch::pin().flush();
}
==17334== LEAK SUMMARY:
==17334==    definitely lost: 0 bytes in 0 blocks
==17334==    indirectly lost: 0 bytes in 0 blocks
==17334==      possibly lost: 76 bytes in 1 blocks
==17334==    still reachable: 20,408 bytes in 12 blocks
==17334==         suppressed: 0 bytes in 0 blocks

1,228 bytes lost

fn main() {
    let cache = moka::sync::Cache::builder()
        .max_capacity(1000)
        // .thread_pool_enabled(false)
        .build();

    for i in 0..1000u32 {
        cache.insert(i, i);
    }

    for i in 0..1000u32 {
        cache.invalidate(&i);
    }

    drop(cache);

    // std::thread::sleep(std::time::Duration::from_secs(1));
    // crossbeam_epoch::pin().flush();
}
==18144==    definitely lost: 0 bytes in 0 blocks
==18144==    indirectly lost: 0 bytes in 0 blocks
==18144==      possibly lost: 1,228 bytes in 5 blocks
==18144==    still reachable: 17,524 bytes in 41 blocks
==18144==         suppressed: 0 bytes in 0 blocks

80,000 bytes lost

This is unexpected (bug).

fn main() {
    let cache = moka::sync::Cache::builder()
        .max_capacity(1000)
        .thread_pool_enabled(false)
        .build();

    for i in 0..1000u32 {
        cache.insert(i, i);
    }

    // for i in 0..1000u32 {
    //     cache.invalidate(&i);
    // }

    drop(cache);

    // std::thread::sleep(std::time::Duration::from_secs(1));
    // crossbeam_epoch::pin().flush();
}
==17642== LEAK SUMMARY:
==17642==    definitely lost: 48,000 bytes in 1,000 blocks
==17642==    indirectly lost: 32,000 bytes in 1,000 blocks
==17642==      possibly lost: 0 bytes in 0 blocks
==17642==    still reachable: 6,888 bytes in 4 blocks
==17642==         suppressed: 0 bytes in 0 blocks

@tatsuya6502
Copy link
Member

Hi @losfair,

Sorry for the bug. I have fixed the last one with "80,000 bytes lost" via #177. Now running your reproducing code with valgrind prints the followings:

=23552== LEAK SUMMARY:
==23552==    definitely lost: 0 bytes in 0 blocks
==23552==    indirectly lost: 0 bytes in 0 blocks
==23552==      possibly lost: 1,228 bytes in 5 blocks
==23552==    still reachable: 11,252 bytes in 37 blocks
==23552==         suppressed: 0 bytes in 0 blocks

For the "possibly lost" stuff, you could mitigate it by calling CacheBuilder's thread_pool_enabled method with false:

let cache = moka::sync::Cache::builder()
    .max_capacity(1000)
    .thread_pool_enabled(false)
    .build();

This method was added to Moka v0.9.3, and false will become the default in a future release (v0.10 or v0.11).

I am going to run pre-release tests, and if they go well, I will publish Moka v0.9.4 (with the fix) to crates.io.

@losfair
Copy link
Author

losfair commented Sep 3, 2022

Thank you so much for the quick fix!

@tatsuya6502
Copy link
Member

I have published Moka v0.9.4 to crates.io.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants