There are still residues in memory?

after having done all the deinit etc…

why virtual memory does not return zero

a little test that shows all that

const std = @import("std");

pub const FIELD = struct {

    regex: [] const u8,   //contrôle regex

  };
  
const allocator = std.heap.page_allocator;
pub var fieldX: std.ArrayList(FIELD) = std.ArrayList(FIELD).init(allocator);

const stdin = std.io.getStdIn().reader();
pub fn main() !void {
  var buf : [3]u8 = undefined;

  var i : usize = 0;
  var buffer : [4096] u8 =  [_]u8{0} ** 4096;

  var zone = FIELD {   

        .regex  = undefined ,

    };
  std.debug.print("stop 1/3 contrôl memoire \r\n",.{});
  buf =  [_]u8{0} ** 3;
  _= try stdin.readUntilDelimiterOrEof(buf[0..], '\n');
  
  var allouer : bool = true ; // allocation 

while ( i  < 100) : ( i += 1 ) {
  if (!allouer ) {
  // bufprintZ fournit une addresse a result du coup result 
  var result =  std.fmt.bufPrintZ(buffer[0..], "^[0-9]{s}1,{d}{s}$",.{"{",i,"}"}) catch unreachable;
  
  zone.regex = result[0..];
  // la solution serait  
  // zone.regex =  allocator.dupe(u8, result) catch unreachable;
  // ce qui reviendrait à faire un allocprint . 
  }
  else {
    zone.regex = std.fmt.allocPrint(allocator, "^[0-9]{s}1,{d}{s}$",.{"{",i,"}"}) catch unreachable;
    //var resultx =  std.fmt.bufPrintZ(buffer[0..], "^[0-9]{s}1,{d}{s}$",.{"{",i,"}"}) catch unreachable;
    //zone.regex =  allocator.dupe(u8, resultx) catch unreachable;
  }
  fieldX.append(zone) catch unreachable;

}

i = 0;
while ( i  < 100) : ( i += 1 ) {
  std.debug.print("{s}  {d}\r\n",.{fieldX.items[i].regex, fieldX.items[i].regex.len});
}

std.debug.print("stop 2/3 contrôl memoire \r\n",.{});
buf =  [_]u8{0} ** 3;
_= try stdin.readUntilDelimiterOrEof(buf[0..], '\n');

if (allouer ) {
  i = 0;
  while ( i  < 100) : ( i += 1 ) {
  allocator.free(fieldX.items[i].regex);
  }
}
  fieldX.deinit();
  std.debug.print("stop 3/3 fin \r\n",.{});
  buf =  [_]u8{0} ** 3;
  _= try stdin.readUntilDelimiterOrEof(buf[0..], '\n');

}

fieldX still has all the FIELDs in it after the while loop that frees the .regexes.

You can call fieldX.clearAndFree() to clear it, or fieldX.deinit() if you’re completely finished with the list.

hich is not enough.

I have the impression that when you declare an allocator there are residuals

Hi JPL, couple questions for you so I can better understand your concern:

What version of Zig are you using? (Please run “zig version” and show us which version you are using).

When you say “declare an allocator” do you mean initialize one by calling the .init function, or do you mean after using an allocator to create allocations? I’m not sure what you mean by declare alone.

If it’s a problem with the allocator itself, can you provide a simpler example that shows the issue with the allocator itself?

not only do I have to free the allocations, but also all the data

while ( i  < 100) : ( i += 1 ) {
allocator.free(fieldX.items[i].regex);
fieldX.items[i].regex  ="";
}

No, that’s not necessary, because fieldX.deinit() gives the entire memory free. If you take a look you will see:

     /// Release all allocated memory.
     pub fn deinit(self: Self) void {
         if (@sizeOf(T) > 0) {
             self.allocator.free(self.allocatedSlice());
         }
     }

There you can see, that each element will be destroyed.

fieldX.deinit();
yes, but in this case, there remains 393.2ko in virtual memory, certainly not accessible but present,there should only be 0ko left

If you also tested what I put then, you will have the proof

const std = @import("std");

pub const FIELD = struct {

    regex: [] const u8,   //contrôle regex

  };
  
const allocator = std.heap.page_allocator;
pub var fieldX: std.ArrayList(FIELD) = std.ArrayList(FIELD).init(allocator);

const stdin = std.io.getStdIn().reader();
pub fn main() !void {
  var buf : [4]u8 = [_]u8{0} ** 4;

  var i : usize = 0;


  var zone = FIELD {   

        .regex  = undefined ,

    };
  std.debug.print("stop 1/3 contrôl memoire \r\n",.{});
  _= try stdin.readUntilDelimiterOrEof(buf[0..], '\n');
  

while ( i  < 100) : ( i += 1 ) {

    zone.regex = std.fmt.allocPrint(allocator, "^[0-9]{s}1,{d}{s}$",.{"{",i,"}"}) catch unreachable;
    
    // idem std.fmt.allocPrint
    //var buffer : [128]u8 = undefined;
    //var resultx =  std.fmt.bufPrintZ(buffer[0..], "^[0-9]{s}1,{d}{s}$",.{"{",i,"}"}) catch unreachable;
    //zone.regex =  allocator.dupe(u8, resultx) catch unreachable;

  fieldX.append(zone) catch unreachable;

}

i = 0;
while ( i  < 100) : ( i += 1 ) {
  std.debug.print("{s}  {d}\r\n",.{fieldX.items[i].regex, fieldX.items[i].regex.len});
}

std.debug.print("stop 2/3 contrôl memoire \r\n",.{});
_= try stdin.readUntilDelimiterOrEof(buf[0..], '\n');

//fieldX.deinit();
i = 0;
while ( i  < 100) : ( i += 1 ) {
  allocator.free(fieldX.items[i].regex);
  fieldX.items[i].regex  ="";
}


std.debug.print("stop 3/3 fin \r\n",.{});
_= try stdin.readUntilDelimiterOrEof(buf[0..], '\n');

}

There it works correctly with ArenaAllocator

const std = @import("std");

pub const FIELD = struct {

    regex: [] const u8,   //contrôle regex

  };
  

pub var arenaPrint= std.heap.ArenaAllocator.init(std.heap.page_allocator);
const  allocatorPrint = arenaPrint.allocator();
//const allocator = std.heap.page_allocator;
pub var fieldX: std.ArrayList(FIELD) = std.ArrayList(FIELD).init(allocatorPrint);

const stdin = std.io.getStdIn().reader();
pub fn main() !void {
  var buf : [4]u8 = [_]u8{0} ** 4;

  var i : usize = 0;


  var zone = FIELD {   

        .regex  = undefined ,

    };
  std.debug.print("stop 1/3 contrôl memoire \r\n",.{});
  _= try stdin.readUntilDelimiterOrEof(buf[0..], '\n');
  

while ( i  < 100) : ( i += 1 ) {

    zone.regex = std.fmt.allocPrint(allocatorPrint, "^[0-9]{s}1,{d}{s}$",.{"{",i,"}"}) catch unreachable;
    
    // idem std.fmt.allocPrint
    //var buffer : [128]u8 = undefined;
    //var resultx =  std.fmt.bufPrintZ(buffer[0..], "^[0-9]{s}1,{d}{s}$",.{"{",i,"}"}) catch unreachable;
    //zone.regex =  allocator.dupe(u8, resultx) catch unreachable;

  fieldX.append(zone) catch unreachable;

}

i = 0;
while ( i  < 100) : ( i += 1 ) {
  std.debug.print("{s}  {d}\r\n",.{fieldX.items[i].regex, fieldX.items[i].regex.len});
}

std.debug.print("stop 2/3 contrôl memoire \r\n",.{});
_= try stdin.readUntilDelimiterOrEof(buf[0..], '\n');

//fieldX.deinit();
//i = 0;
//while ( i  < 100) : ( i += 1 ) {
//  allocator.free(fieldX.items[i].regex);
//  fieldX.items[i].regex  ="";
//}
fieldX.clearAndFree();

std.debug.print("stop 3/3 fin \r\n",.{});
_= try stdin.readUntilDelimiterOrEof(buf[0..], '\n');

}
1 Like

Sorry, but I can’t understand this. Apart from the fact that you have some blunders in your programming, you use fieldX.clearAndFree in the heap allocator to clean up, which in addition to the memory enable also sets the counter of the slice to 0. But this doesn’t matter, because it has no effect on the memory. It only confuses when you want to compare something, and you call different functions.

In generally you should program an allocator as follows in the main function:

pub fn main() !void {
    const allocator = std.heap.page_allocator;
    var fieldX = std.ArrayList(FIELD).init(allocator);
    defer fieldX.deinit();

So immediately after the initialization, call deinit() using defer. Thus the compiler always ensures the correct call of the release.

Maybe you can post the output of your console so I understand what you mean?

2 Likes
const allocator = std.heap.page_allocator;
.....

fieldX.deinit();
std.debug.print("test 3/3 contrôl memoire \r\n",.{});

pub var arenaPrint= std.heap.ArenaAllocator.init(std.heap.page_allocator);
const  allocatorPrint = arenaPrint.allocator();
....
fieldX.deinit();

two examples one as you recommended
and one with an arena
2 different answers
what you say with an anrena is correct but not with const allocatorPrint = std.heap.page_allocator;

I want to be able to have resets without leaving the program it could have been modules

Sure, you can use something like this:

fn testMemory(allocator: std.mem.Allocator) !void {
    var fieldX = std.ArrayList(FIELD).init(allocator);
    defer fieldX.deinit();

    var buf: [3]u8 = undefined;

    var i: usize = 0;
    var buffer = [_]u8{0} ** 4096;

 ...

pub fn main() !void {
    const allocator = std.heap.page_allocator;
    try testMemory(allocator);

    std.debug.print("stop 3/3 fin \r\n", .{});
    var buf = [_]u8{0} ** 3;
    _ = try stdin.readUntilDelimiterOrEof(buf[0..], '\n');
}

But anyway, I get no different result. What OS do you use?

DISTRIB_ID=“ManjaroLinux”
DISTRIB_RELEASE=“23.0.0”
DISTRIB_CODENAME=“Ultima Thule”
DISTRIB_DESCRIPTION=“Manjaro Linux”

zig-linux-x86_64-0.11.0-dev.3934+ba6e5e65a

More or less the same what I’m using. Which tool do you use for monitoring? I use htop.

System Monitor

I thank you for the efforts you have made to try to understand why the two ways of doing things give two different results… because I don’t understand.

ps:

I would like there to be a step-by-step article of how to debug, and if there is a debugger a bit Kdbg or DDD or Native VSCode debugger. Supports both GDB and LLDB.

Now I also installed this program and indeed, it shows memory after deinit while htop does not. Weird.

I have to correct me, was my bad, now I see the memory consumption in htop too. And now back to your first question: why is that? I really don’t know and when I find some time I will investgate a bit. Maybe it is a problem in Zig, maybe it is a problem in monitoring. Interesting.

2 Likes

thank you, I’m not crazy, I keep my little program to see if there are developments.

now I think it should not be obvious to make a compiler with all that it requires. I computerized companies from A-Z I looked seriously at the fundamentals and I take my hat off to those who make a language worthy of the name.

1 Like