Here is simple C program which prints some addresses
#include <stdio.h>
#include <stdlib.h>
struct thing {
int a;
int b;
};
int main(void)
{
struct thing t1, t2, *t3, *t4;
printf(" t1 @ %p\n", &t1);
printf(" t2 @ %p\n", &t2);
printf(" t3 @ %p\n", &t3);
printf(" t4 @ %p\n", &t4);
t3 = calloc(1, sizeof(struct thing));
t4 = calloc(1, sizeof(struct thing));
printf("*t3 @ %p\n", t3);
printf("*t4 @ %p\n", t4);
}
Output:
t1 @ 0x7ffcbecee138
t2 @ 0x7ffcbecee140
t3 @ 0x7ffcbecee128
t4 @ 0x7ffcbecee130
*t3 @ 0x56153fdc16b0
*t4 @ 0x56153fdc16d0
From the output I clearly see that all 4 variables are on the stack and t3, t4 points somewhere into the heap (addresses with lower values). Then I tried to write something similar in Nim:
from strutils import toHex, strip
proc printAddr(p: pointer) =
let u = cast[uint64](p)
echo "0x", u.toHex.strip(leading = true, trailing = false, chars = {'0'})
type
Thing = object
a: int
b: int
var
t1 = Thing(a: 1, b: 2) # on the stack
t2 = Thing(a: 3, b: 4) # on the stack
t3, t4: ref Thing # on the heap
printAddr(t1.addr)
printAddr(t2.addr)
t3 = new(Thing)
t4 = new(Thing)
echo t3.repr
echo t4.repr
Output:
0x55F7A66C3060
0x55F7A66C3070
ref 0x7f7e3c893050 --> [a = 0, b = 0]
ref 0x7f7e3c893070 --> [a = 0, b = 0]
First two addresses seem to belong to the heap, aren't they? I expected them to be in the stack region. Second two other way around... If these are addresses of t3, t4 (which are on the stack), then how to print locations where the objects seat?
I'm too lazy to really bother how you use addr
Just print them, nothing more. BTW, is there some less fancy way to do that?
but your understanding is correct, things are on the stack unless it's ref. Yes, really.
In C example addresses of variables on the stack are greater then heap addresses and this is Ok, usually the stack resides 'below' the heap, at higher addresses, right? In NIm example I see reversed picture - variables on the stack (non ref) have lower addresses - this is the point of my confusion.
Make them local to proc and they become stack.
Yes, they did:
0x7FFF28EE50E0
0x7FFF28EE50F0
ref 0x7f9eed9d1050 --> [a = 0, b = 0]
ref 0x7f9eed9d1070 --> [a = 0, b = 0]
Thanx! I just thought that everything that is not in procs is a sort of implicit main
But what about t3 and t4? If repr actually prints heap addresses then how to print addresses of these references (I moved them to main proc also)?
proc main() =
var
t1, t2: Thing
t3, t4: ref Thing
printAddr(t1.addr)
printAddr(t2.addr)
t3.new
t4.new
echo t3.repr
echo t4.repr
main()
But what about t3 and t4?
Maybe this helps:
type
Thing = object
a: int
b: int
proc main() =
var
t1, t2: Thing
t3, t4: ref Thing
echo cast[int](t1.addr) # address of t1 object on the stack
echo cast[int](t4.addr) # address of t4 ref on the stack (ref is managed pointer)
echo sizeof(t1) # 16 on 64 bit OS
echo sizeof(t4) # size of a pointer type, 8 on 64 bit OS
echo cast[int](t4) # value of ref (pointer), still nil
t4 = new Thing
echo cast[int](t4) # now != nil
echo cast[int](t4[].addr) # addr of the t4 object on the heap
main()
140726810411408
140726810411392
16
8
0
140277376020560
140277376020560
I am not sure if I have confused something -- you may read one of the books for details.
I am not sure if I have confused something
Not at all. It seems now everything is clear for me. Thanx once again. The only thing that is "interesting" - heap addresses in Nim are much more closer to the stack top than in C example.
Final Nim variant, just in case:
from strutils import toHex, strip
proc printAddr(p: pointer) =
let u = cast[uint64](p)
echo "0x", u.toHex.strip(leading = true, trailing = false, chars = {'0'})
type
Thing = object
a: int
b: int
proc main() =
var
t1, t2 : Thing
t3, t4: ref Thing
printAddr(t1.addr)
printAddr(t2.addr)
printAddr(t3.addr)
printAddr(t4.addr)
t3.new
t4.new
printAddr(t3[].addr)
printAddr(t4[].addr)
echo t3.repr
echo t4.repr
echo sizeof(t3)
main()
you may read one of the books for details
Oh, yes... I've just started to read your book and found answers for some burning questions at once :)
And why did struggle along with printing addresses as hex?... :)
proc printf(formatstr: cstring) {.header: "<stdio.h>", varargs.}
type
Thing = object
a: int
b: int
proc main() =
var
t1, t2 : Thing
t3, t4: ref Thing
printf(" t1 @ %p\n", t1.addr)
printf(" t2 @ %p\n", t2.addr)
printf(" t3 @ %p\n", t3.addr)
printf(" t4 @ %p\n", t4.addr)
t3.new
t4.new
printf("*t3 @ %p\n", t3[].addr)
printf("*t4 @ %p\n", t4[].addr)
main()
btw, t3[].addr construction looks a little bit strange ('dereference and then take address'), in C I just print the pointer value.
The Linux kernel uses reference counting (RC)
Yes, but it is not about kmalloc() - it is pure MMM. It is about counting how many times a file was opened or so.
than your uneducated opinions and feelings.
Nice to hear, but what do think about the origins of GC? I mean the idea, not the algorithms.
The Linux kernel uses reference counting (RC) and RC is a GC algorithm
Here my 'uneducated opinion' tells me that you are mixing RAM (as an intrinsic part of a computer) with peripheral devices, which are not 'malloc/free' things. :-P
Er, the kernel uses RC not just for the file system...
but not for kmalloc(), right? ,)
You really are uneducated, sorry.
You are really did not write hardware drivers for Linux, otherwise you wouldn't tell shit about RC in Linux kernel, sorry :)
As for where the algorithm came from, it's everywhere. "The last one who leaves turns off the light".
very funny, ha-ha-ha )
The Linux kernel uses reference counting (RC) and RC is a GC algorithm
You did not answer the actual question. It was not about GC per se. Please, do not shirk. Does Nim runtime uses two separate regions of heap?
The markAndSweep, refc, and arc/orc memory management strategies use the same allocator and heap for garbage-collected objects as are used for manual memory management. For markAndSweep and refc the top-level management structure GcHeap (which has a static size) is a thread-local global variable, but the dynamic parts also use the allocator.
Both alloc (manual memory management) and newObj (used internally for creating garbage-collected objects) use rawAlloc with gch.region as the MemRegion.
You can verify this yourself by looking into lib/system/gc.nim, lib/system/gc_ms.nim, and lib/system/alloc.nim for the refc GC, markAndSweep GC, and allocator implementation respectively.
The markAndSweep, refc, and arc/orc memory management strategies use the same allocator and heap for garbage-collected objects as are used for manual memory management.
Thanx for the clarification. From the code below it's clear that
proc printf(formatstr: cstring) {.header: "<stdio.h>", varargs.}
proc malloc(n: int): pointer {.header: "<stdlib.h>".}
type
Thing = object
a: int
b: int
var
t0: Thing
proc main() =
var
t1: Thing
t2: ptr Thing
t3: ptr Thing
t4: ref Thing
printf(" t0 @ %p (data segment)\n", t0.addr)
printf(" t1 @ %p (stack)\n", t1.addr)
t2 = cast[ptr Thing](malloc(Thing.sizeof))
t3 = cast[ptr Thing](alloc(Thing.sizeof))
t4.new
printf("*t2 @ %p (heap 0)\n", t2)
printf("*t3 @ %p (heap 1)\n", t3)
printf("*t4 @ %p (heap 1)\n", t4)
main()
And the output is
t0 @ 0x5621668775e0 (data segment)
t1 @ 0x7fff1747e260 (stack)
*t2 @ 0x5621669156b0 (heap 0)
*t3 @ 0x7f78d8e6b050 (heap 1)
*t4 @ 0x7f78d8e6b070 (heap 1)
What's your point, is this some kind of blocker or bug?
Neither, I'm just learning the language (with no some special purpose)