Rust LinkedList pop_front do not free its Memory

Today , i am testing if my own code exists memory leak problem ,cause i use some unsafe rust. i try to insert node to linked-list and pop it out . As my code , once node was popped , it should be free , but i make a breakpoint after the all pop , i check my memory usage (using free -hm ), it still leave a high usage , and i use the std::collections::LinkedList as another test , i found memory usage still high ,i wanna know why :ghost:

Here is my code own

use std::boxed::Box;
use std::io;
use std::ptr::NonNull;
use std::time::Instant;
const NODE_NUMBER: usize = 30_00_00_00_0;
fn main() {
    // let mut v = LinkList::new();
    // v.push_front(12);
    // v.push_front(12);
    // let res = v.pop_front();
    // println!("{}",res.unwrap());
    let mut l = LinkList::new();
    let now = Instant::now();
    for i in 1..NODE_NUMBER {
        l.push_front(i as i32);
    }
    let mut line = String::new();
    //for _ in 1..NODE_NUMBER{
    //    l.pop_front();
    //}
    l.clear();
    io::stdin().read_line(&mut line).unwrap();

    println!("{} costs {} mills ", NODE_NUMBER, now.elapsed().as_millis());
}
#[test]
fn test1() {
    let mut l = LinkList::new();
    l.push_front(12);
    l.push_front(-12);
    l.push_front(0);
    assert_eq!(l.pop_front(), Some(0));
    assert_eq!(l.pop_front(), Some(-12));
    assert_eq!(l.pop_front(), Some(12));
}
#[test]
fn test2() {
    let mut l = LinkList::new();
    l.push_front(12);
    l.push_front(10);
    assert_eq!(l.as_vec().unwrap(), vec![10, 12]);
}
type LINKNODE = Option<NonNull<Node>>;
struct LinkList {
    head: LINKNODE,
    tail: LINKNODE,
    len: usize,
}
#[derive(Debug)]
struct Node {
    elm: i32,
    next: LINKNODE,
}
impl Node {
    fn from(elm: i32) -> Self {
        Node { elm, next: None }
    }
}
//impl Drop for Node{
//    fn drop(&mut self){
//       //println!("I am dropped");
//    }
//}
impl Drop for LinkList {
    fn drop(&mut self) {
        self.clear();
    }
}
impl LinkList {
    pub fn new() -> Self {
        LinkList {
            head: None,
            tail: None,
            len: 0,
        }
    }
    pub fn push_front(&mut self, elm: i32) {
        self.push_front_elm(elm);
        self.len += 1;
    }
    pub fn pop_front(&mut self) -> Option<i32> {
        if self.len <= 0 {
            return None;
        }
        match self.drop_front_elm() {
            None => None,
            Some(v) => {
                self.len -= 1;
                return Some(v);
            }
        }
    }

    //pub fn pop_front(&mut self)->Option<Box<Node>>{
    //    if self.len <=0{
    //        return None;
    //    }
    //    match self.drop_front_elm(){
    //        None=>None,
    //        Some(v)=>{self.len -=1;return Some(v);}
    //    }
    //
    //}
    pub fn clear(&mut self) {
        if self.len <= 0 {
            return ();
        }
        let mut ptr = self.head.unwrap().as_ptr(); // move NonNull
        loop {
            unsafe {
                self.head = (*ptr).next;
                // std::ptr::drop_in_place(ptr); // it will can old drop fn
                let _ = Box::from_raw(ptr);
                //dealloc(ptr,);
            }
            self.len -= 1;
            if self.head.is_none() {
                break;
            }
            ptr = self.head.unwrap().as_ptr();
        }
    }
    fn drop_front_elm(&mut self) -> Option<i32> {
        let old: NonNull<Node> = self.head.unwrap(); // it cannot move  it copy
        let old_ptr = old.as_ptr(); // *mut Node
        unsafe {
            self.head = (*old_ptr).next;
            let n = (*old_ptr).elm;
            let _ = Box::from_raw(old_ptr);
            return Some(n);
        }
    }

    //    fn drop_front_elm(&mut self)->Option<Box<Node>>{
    //        let old : NonNull<Node> = self.head.unwrap(); // it cannot move  it copy
    //        let old_ptr = old.as_ptr(); // *mut Node
    //        let node: Box<Node>;
    //        unsafe{
    //            node = Box::from_raw(old_ptr); // create a new one
    //            self.head = (*old_ptr).next;
    //            std::ptr::drop_in_place(old_ptr);
    //        }
    //        Some(node)
    //
    //    }
    //
    fn push_front_elm(&mut self, elm: i32) {
        let node: Box<Node> = Box::new(Node::from(elm));

        let node_ptr: NonNull<Node> = NonNull::from(Box::leak(node));
        let ptr = node_ptr.as_ptr();
        unsafe {
            (*ptr).next = self.head;
            (*ptr).elm = elm;
            if self.len == 0 {
                self.tail = Some(node_ptr);
            }
            self.head = Some(node_ptr);
        }
    }

    pub fn as_vec(&self) -> Option<Vec<i32>> {
        if self.len <= 0 {
            return None;
        }
        println!("len -> {}", self.len);
        let mut ptr: *mut Node = self.head.unwrap().as_ptr();
        let mut vec = Vec::with_capacity(self.len);
        loop {
            unsafe {
                vec.push((*ptr).elm);
                if (*ptr).next.is_none() {
                    break;
                }
                ptr = (*ptr).next.unwrap().as_ptr();
            }
        }
        return Some(vec);
    }
}

Here is the std::collections::LinkedList i use

use std::collections::LinkedList;
use std::io;
use std::time::Instant;
const NODE_NUMBER: usize = 30_00_00_00_0;

fn main() {
    let mut l = LinkedList::new();
    let now = Instant::now();
    for i in 1..NODE_NUMBER {
        l.push_front(i as i32);
    }
    for i in 1..NODE_NUMBER {
        l.pop_front();
    }
    println!("Trace the Memory Usage");
    let mut line = String::new();
    io::stdin().read_line(&mut line);
    println!("{} consts {} mills", NODE_NUMBER, now.elapsed().as_millis());
}

Using free won't work for determining whether memory is freed by your program, because it is looking at system memory usage. The memory allocator used by Rust (and most all programs) won't necessarily return memory to the system when it is freed by your program. For one thing, it allocates memory from the system in large blocks, and provides the memory you request out of those large blocks. For another thing, it may not return these large blocks to the system immediately when they are empty.

To determine whether you have a leak due to unsafe code, write lots of good tests and run those tests under Miri. Miri checks for leaks due to the use of unsafe code. It also checks for other problems with unsafe code, although it may not detect all such problems.

1 Like

i am so stupid !!! i try to write the same program using cpp , then i realize i should not use free to CHECK IT , cause the memory not actually free to system :black_joker:
i am going to download Miri !!!!!!!!!!!!

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.