Memory |
The name of a local variable, parameter, or instance variable represents a location in memory, generally one kept on the run-time stack. In Java, values associated with variables are held in one of two ways. If a variable has a primitive type, then the value associated with the variable is held in the corresponding memory location.
On the other hand if a variable has an object type, then the value associated with the variable is a reference to an object from the heap. That is the actual value stored in the variable's location is the address of memory on the heap.
Memory is allocated either on the run-time stack or in the heap. Values of parameters and local variables are kept on the stack, while objects (and hence their instance variables) are allocated space in the heap.
Values on the stack (e.g., parameters and local variables) are associated with activation records. They are allocated space automatically when a function or method is activated, and are popped of when the function or method returns.
Values in the stack, however, are allocated as a result of a "new" statement. In Java, they are "garbage collected" in that a process comes through and recycles all slots in the heap that are inaccessible from variables represented in the run-time stack.
Suppose we have a Java class Point with int instance variables x and y. In Java, when you execute a statement of the form
Point p = new Point(\ldots);
the system first allocates sufficient space for all the instance variables of Point (and a reference to its methods, which are shared by all objects of the class). It then executes all initialization code, including the body of the constructor. It then stores the address of the beginning of the object's memory in the location associated with variable p.
For example, suppose variable b is allocated on the stack at location 100 and the above statement is executed. Then memory is allocated from the heap for the object. Suppose this starts at location 1011. Then each of the slots corresponding to instance variables is initialized, and that location is stored in b. Thus the actual value stored in b is 1011. When you assign a value to an instance variable of b, the system goes to location 1011, calculates the appropriate
When there is an assignment of objects in Java, generally only the location is copied. E.g., if q is in location 107 then
q = p;
results in q also holding the location 1011. That is, both p and q now refer to the same object. If a message setX(47) is sent to p then both p and q will both see that the x field has value 47. On the other hand if we now execute
p = new Point(...);
where the new value is at location 1026 then p will refer to that new object, but q will not.
Things work similarly in C++, except that it has no garbage collector. Thus, when you are done with memory allocated on the heap, you must explicitly deallocate it.
References or pointers must be explicitly created in C++. Compare:
int x, y; int *p, *q; p = new int; // p now points to an int *p = 47; // p now points to 47 q = p; // q points to the same int as p *q = 23; // both p and q point to 23 p = NULL; // p points to nowhere p = &x // p points to the location corr to x
The last is very dangerous if x is allocated on the stack, as p will point to unallocated memory if the stack frame containing x is popped. It is good practice never to have a pointer to anything on the stack (try to avoid &x in assignments).
Anything created with new must be deallocated with delete or you will get memory leaks. E.g., delete p will recycle memory in the heap pointed at by p.
Unfortunately, there are many opportunities for pointer errors. We've pointed out memory leaks that result from forgetting to recycle memory, but worse problems can result from deallocating memory that is still in use. For example,
int *p, *q; p = new int; q = p; delete q; ... p ...
Because p and q both refer to the same slot in the heap, p continues to refer to the same slot in memory that q deleted, but after some time, that memory may be reallocated to another pointer and filled with something completely different. This is known as a dangling pointer and must be avoided. By the way, the value of q is considered to be undefined after it has been deleted. That is, one may not count an anything being true about it after it has been deleted.
A good strategy in dealing with the heap is to first write your program without using any delete statements and then add them slowly until everything is taken care of. Unfortunately, you will likely make mistakes one way or another. It is estimated that over 50% of programmer errors are due to pointer problems.
Memory |