This doesn’t really have that much to do with Java or proramming in general… It is just a question that has been on my mind lately and this is probably the best place to ask… I already know how memory works in binary and how there is read and write commands and busses… But I want to know, when the program makes a new value to hold in memory, the memory assigns a location to put that value. But how does the program know where that location is? And how does the program remember that? Another memory point? How does this work?
Program: Dear Memory, I have this value 12345, do you have somewhere to store it? Memory: Dear Program, no problemo, I’ve put it at address 0x0010fca0, you can use that address in future to get it Program: Dear Memory, cheers, I’ll remember that address!
Later:
Program: Dear Memory, please can you get me a value back, here’s the address you gave me earlier: 0x0010fca0 Memory: Too easy mate, the value there is 12345 Program: Great, thanks!
Moral of the story: Variables in programs are memory addresses.
During compilation the compiler keeps a lookup table of the human names we’re using and the memory addresses that the memory manager allocated, so everywhere it comes across the same variable name again it knows the address it was given the first time. Later when the program is run, the human names are irrelevant and the language runtime (e.g. JVM for Java) just uses the addresses.
If you google how compilers work I’m sure you’ll find some nice diagrams showing that much clearer than I can say it! Compilers are a fascinating subject and not nearly so difficult as they’re sometimes made out
I have to nit-pick here, that the program is not asking the memory, but the operating system to allocate a chunk of memory and gets back the address of that location.
But that doesnt make sense…
How does the program remember that address? Another memory address? Does that address stay with the compiled code? So if two programs use the same memory address, what happens then?
I’m not entirely sure, but that’s how I think it is:
In the program assembly code there is a defined memory adress, for example addres 0x0000A001. Each program assembly is a single process in the operating system. So if the OS is currently processing that program and it request to read/write a value in 0x0000A001, it is actually written in RAM at a specific offset. That offset is different in different processes (= programs).
As I said - I’m not entirely sure about that. But I am sure that each program (= process) has it’s own part of RAM. It’s the OS’s duty to manage those.
It depends on what you’re talking about. Say program addresses (part of the code)…it depends on object file format and opcode type. Some opcodes will be relative offsets. Others are fixed offsets. For fixed offsets then the object format is either fixed or relocatable. If relocatable then the exact addresses are resolved at load time. Modern OSes and virtually memory (part of the CPU) give a virtual memory space to each process, so each is independent from all the rest…in other words address X in process 0 and address X in process 1 are not the same physical address (exceptions here for shared accesses, but we’ll ignore that).
If you’re talking about data in a program…then ‘pointer’ is what you’re asking for.
That’s basically it. The OS makes it so that as far as each running program is concerned, it has the whole addressable memory to itself (on Win32 that’s 2Gb of “address space”).
But in reality there may be many programs running, and indeed the computer may not actually have 2Gb of memory even though it’s pretending it does. The OS hides all that - it’s called “virtual memory” (http://en.wikipedia.org/wiki/Virtual_memory) and the addresses the program is given are virtual addresses.
Then Java programs are even more complicated because the JVM’s another layer of memory management on top (with garbage collection and stuff). Like an onion - there’s always another layer underneath
(DISCLAIMER: Possibly a lot of inaccurate information below. I’d like to think I have an almost decent idea of how memory works, but I don’t know what I don’t know. If I said something wrong, please correct me, and please research for yourself before trusting anything I have said)
Depends on whether it’s stack or heap allocated.
A stack is just a block of memory, usually one per thread. When a function is called it is responsible for returning the stack to the state it was in at the start of the function. So when something is stack allocated, the compiler knows exactly where each piece of data is going to be relative to the top of the stack. Therefore the opcodes required to access the stack memory are all determined at compile time and aren’t actually stored in memory at all (unless you count the opcodes themselves).
The advantages of stack allocation are speed and no garbage collection requirement. When a function exits, all the stack allocations it made are automatically deleted, so any persistent data needs to be copied off the stack to another location (in the case of return values, they usually end up back on the stack). Also, the stack can only be used to store data for which the size is known at compile time. Lots of the time, especially in games, the amount and size of the data cannot be determined until runtime.
This is where heap allocation comes in. The heap is really just a ‘heap’ of memory. This is memory managed by the system and sort-of ‘lent out’ to programs. When something is heap allocated, a call is sent to the system asking for a pointer to block of memory of whatever size. If that memory is available, it returns the memory location of the start of that chunk of memory, also know as a pointer. This pointer is just an integer value (on modern machines, 32- or 64-bit depending on the CPU architecture), and so is stored on the stack like any other value (or, stored in the heap, resulting in a pointer to a pointer). This memory can be freely passed around between functions as a pointer on the stack, but it must be explicitly freed (via another system call) or else the computer will eventually run out of memory.
This is where the main problem of manual memory management comes in. You always have to make sure that memory is freed exactly once, and never used after it is freed. However, when you’re passing copies of the same pointer all over the place it becomes hard to tell when exactly that should be. Free it too early and you’ll most likely get segfaults when you try to access it later. Forget to free it and you get memory leaks.
There are several ways this problem is overcome in modern languages. The most popular is garbage collection, but this comes at a high price. Lots of CPU cycles are used tracking where each piece of data is being used, as well as detecting and responding to cycles (A contains the last pointer to B which contains the last pointer to A, etc. so while neither are really being used, it’s not that obvious). Other alternatives include simpler but incomplete forms of garbage collection (eg: reference-counting), or using unique pointers (a single reference to a memory location that automatically frees it when the pointer is freed). These incomplete alternatives still require a bit of manual intervention, but they do make the job a bit easier.
In Java, everythingobjects are heap allocated. This causes lots of pains with the garbage collector as data that could be easily freed as soon as the method exits instead has to be collected like anything else.
If all this post hasn’t scared you away from manual memory management and you have a lot of spare time on your hands, I recommend having a look at Rust. It’s a language that has no runtime automatic memory management (and so is fast), but instead enforces that you don’t do anything stupid at compile time (so no segfaults or memory leaks). If you’re interested, maybe read this reddit comment about a Java/Scala developer’s experience with Rust.
In Java*
In C++/C (and maybe some other languages) you have to tell the program what’s a pointer and what isn’t. By default it isn’t. In Java technically everything is a pointer.
Just to be clear, Objects are heap allocated. Primitives (like ints, doubles and pointers) are (since their size is known) allocated on the stack.
Computerphile has a video on stacks and why they are fundamental to how we do computation: https://www.youtube.com/watch?v=7ha78yWRDlE
If you liked that, you should go and watch as much of their other stuff as you have time for. It’s good stuff.
AFAIK, the escape analysis is there and it is possible to determine whether an object could be stack allocated, but I am yet to see a JVM that actually does it.
I would recommend picking up a simple educational assembly language. It will help you understand these things way better. But yes, when you request to allocate memory from the OS it simply returns you a pointer to the first block of memory you allocated. You keep track of this block in your program. (if you have an OS, if not or if it is OS running code then you just use whatever memory locations you please, and you program your program to not cause memory over runs, etc.)
A heap is purely user-side code. It’s a rough classification of a type of data-structure for managing memory. Specifically there be a boundary tag (or similar) associated with each chunk that the heap is aware of. All GCs are heap based except semi-space. Almost all memory managers provided by a language runtimes are heaps. Neither the OS nor CPUs care about heaps.
Am I missing something? I was under the impression that this was enabled by default since 2009! :persecutioncomplex: OK, technically this is scalar replacement not stack allocation, but in this context (not user defined) what would be the difference? What benefits are missing?
If you want to truly understand how this stuff works you need to learn assembler and write some (small simple) programs in it. Looking at C and what kind of assembler code a C compiler produces helps too.
I recommend you work through Computer Science 61C
So I’m guessing it is similar to how windows works… When you draw at 0,0 it doesn’t actually draw at the actual 0,0 but it draws at 0,0 on the window assigned to the program…
In the same way the memory address 0 isn’t at 0 but it is at 0 on the assigned memory group…
Am I right? (Or am I right [aw yeeeaaah 8) ])
Also, I tried learning C++ but I got completely stumped and after a few weeks of complete anoying pain I googled why C++ sucks… It was fun… I guess I am just used to Java