Java equivalents of malloc(), new, free() and delete
C++ provides a few ways to allocate memory from the
heap1:
- the new operator will allocate and initialise an instance
of a particular object or array;
- the malloc() function, inherited from C, will allocate
an arbitrary block of memory which can then be used
for any purpose, such as an array or struct.
In either case, C++ also requires the programmer to free up allocated memory
once it is no longer needed (or else have a memory leak). The free()
function recovers memory allocated via malloc(). The
delete keyword deconstructs an object, which
could involve some cleanup code in addition to deallocating the memory.
To understand the Java equivalents of these various operations, it's
worth first seeing a general overview of memory management in the two
languages, which we'll do in the next section.
Then in the following sections, we'll look at details of each of the four
abovementioned operations in turn.
The basics: comparing memory management in C++ and Java
For a C++ program to function correctly, the programmer is effectively required
to allocate memory in one of the above ways if it is to escape the function in
which it is allocated. For example, the following is incorrect:
// Warning: do not try this at home!
char *makeString() {
char[] str = "Wibble";
return str;
}
The problem is that str will be allocated off the stack,
and this memory will become invalid (or at least, liable to be overwritten)
when the function exits. To correct the above function, we need to make it
allocate the space for the string via malloc() (or new):
char *makeString() {
char *str = (char *) malloc(200);
strcpy(str, "Wibbly");
return str;
}
It would then be up to the caller to call free() on the
resulting string as and when it had finished with it. Things pretty much have
to be this way in C/C++: in all but the most trivial cases, it would
be virtually impossible for the compiler to determine that an object was
"finished with". (Consider, for example, that in C/C++ any pointer could
be made to point to that object at some arbitrary moment.)
In Java, things are a little different. The JVM sees Java objects as "objects",
not simply as accesses to memory locations. And indeed, Java doesn't allow
access to any memory that isn't part of an object. If there are no more
references to a particular object, the Java Virtual Machine (JVM) knows that
there won't be some pointer lurking about waiting to access that object's memory
space.
This all means that in Java, memory allocation can be managed much more by the runtime system:
- the JVM can work out whether
an object escapes the method or not, and so the JVM can decide where to
allocate the memory (stack, heap, possibly even registers...);
- the JVM can handle garbage collection: that is,
it can work out when an object is no longer
referenced and can thus be "deleted";
- Java basically has no such thing as a pointer2 in the true sense:
all objects are accessed by a reference, but this is not
an actual memory location.
We've said that the JVM can decide to allocate memory for an object
on the stack (if it determines this is safe and desirable). But this is
really an implementational detail.
In terms of how it looks to the programmer, in Java,
all objects are effectively
allocated from the heap.
Next: functions and operators
With this introduction in mind, we'll look at the effective
equivlents of the C++ memory management keywords and functions mentioned:
- the new operator in C++/Java;
- the Java equivalents of the delete operator;
- how to do a malloc() in Java— is that even possible...?
1. A heap is a block of contiguous memory set aside that can
have smaller portions of it allocated out as and when necessary. In the eyes
of the programmer, in both C/C++ and Java, we usually talk about allocating
from "the heap" in an abstract way. But in neither language is there usually
one single block of contiguous memory from which all allocations are made.
For performance reasons,
good malloc() implementations will internally manage several heaps
(e.g. one for large allocations, one for small allocations...); and modern
JVMs often have several heaps internally. In most of this discussion, we'll
continue to use the term "the heap", as that is how memory is presented to
the programmer.
2. The only slight exception in Java is that using the Java Native Interface
(JNI), it possible to wrap a direct ByteBuffer around a block of memory allocated from native code (whose address is thus known). But
the memory address still isn't exposed at the Java level, and from Java, the
memory must still be accessed via that ByteBuffer object.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.