Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Java Memory Management

You're reading from   Java Memory Management A comprehensive guide to garbage collection and JVM tuning

Arrow left icon
Product type Paperback
Published in Nov 2022
Publisher Packt
ISBN-13 9781801812856
Length 146 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (2):
Arrow left icon
Maaike van Putten Maaike van Putten
Author Profile Icon Maaike van Putten
Maaike van Putten
Dr. Seán Kennedy Dr. Seán Kennedy
Author Profile Icon Dr. Seán Kennedy
Dr. Seán Kennedy
Arrow right icon
View More author details
Toc

Table of Contents (10) Chapters Close

Preface 1. Chapter 1: Different Parts of the Java Memory 2. Chapter 2: Primitives and Objects in Java Memory FREE CHAPTER 3. Chapter 3: Zooming in on the Heap Space 4. Chapter 4: Freeing Memory with Garbage Collection 5. Chapter 5: Zooming in on the Metaspace 6. Chapter 6: Configuring and Monitoring the Memory Management of the JVM 7. Chapter 7: Avoiding Memory Leaks 8. Index 9. Other Books You May Enjoy

Storing objects on the heap

In this section, we are going to examine storing objects on the heap. Gaining a full understanding of this area requires a discussion comparing references and objects. We will examine their types, where they are stored, and crucially, their differences. A sample piece of code with an associated diagram will finish the section.

References

References refer to objects and enable us to access them. If we are accessing an object instance member, then we use the reference. If we are accessing a static (class) member, we use the class name.

References can be stored on both the stack and the heap. If the reference is a local variable in a method, then the reference is stored on the stack (in the local method array for that method frame). If the reference is an instance variable, then the reference is stored inside the object, on the heap.

By way of comparison with objects, we can have a reference of an abstract class but not an object of an abstract class. The same applies to interfaces – we can have an interface reference type, but you cannot instantiate an interface; that is, you cannot create an object of an interface type. Both situations are demonstrated in Figure 2.1:

Figure 2.1 – Object instantiation errors

Figure 2.1 – Object instantiation errors

In Figure 2.1, the references declared on lines 10 and 13, an abstract class and an interface reference, respectively, have no issue. However, attempting to create an object of these types on lines 11 and 14 causes errors. Feel free to try out this code, contained in ch2 folder here: https://github.com/PacktPublishing/B18762_Java-Memory-Management/tree/main/ch2. The reason for the compiler errors is that you cannot create an object based on an abstract class or interface. We will address these errors in the next section.

Now that we have discussed references, let us examine objects.

Objects

All objects are stored on the heap. To understand objects, we must first understand a fundamental construct in OOP, the class. A class is similar to the plan of a house. With the plan of the house, you can view it and discuss it, but you cannot open any doors, put the kettle on, and so on. This is what classes are in OOP – they are views of what the object will look like in memory. When the house is built, you can now open the doors, have a cup of tea, and so forth. When the object is built, you have an in-memory representation of the class. Using the reference, we can access the instance members using the dot notation syntax.

Let us address the compiler issues from Figure 2.1 and, in addition, show the dot notation syntax in operation:

Figure 2.2 – The interface and abstract class references fixed

Figure 2.2 – The interface and abstract class references fixed

In Figure 2.2, as lines 11 and 15 compile without any error, they demonstrate that the class must be a non-abstract (concrete) class before an object based on it can be instantiated (created). Lines 12 and 16 demonstrate the dot notation syntax.

Let us now examine in more detail the creation of an object.

How to create objects

Objects are instantiated (created) using the new keyword. The purpose of new is to create an object on the heap and return its address, which we store in a reference variable. Line 11 from Figure 2.2 has the following line of code:

h = new Person();

The reference is on the left-hand side of the assignment operator – we are initializing an h reference of type Human.

The object to be instantiated is on the right-hand side of the assignment operator – we are creating an object of type Person, and the default Person constructor is executed. This default constructor is synthesized by the compiler (as there is no explicit Person constructor present in the code).

Now that we have looked at both objects and references, let us expand the example and, using a diagram, view both the stack and heap representations.

Understanding the differences between references and objects

In order to contrast the stack and the heap, both the Person class and the main() method have been changed:

Figure 2.3 – Stack and heap code

Figure 2.3 – Stack and heap code

Figure 2.3 details a Person class containing two instance variables, a constructor taking two parameters, and the toString() instance method. The second class, StackAndHeap, is the driver class (it contains the main() method). In main(), we initialize a local primitive variable, x, and instantiate an instance of Person.

Figure 2.4 shows the stack and heap representations after line 27 has been executed:

Figure 2.4 – A stack and heap representation of the code in Figure 2.3

Figure 2.4 – A stack and heap representation of the code in Figure 2.3

Referring to Figure 2.3, the first method to execute is main() on line 23. This results in a frame for main() being pushed onto the stack. The local variables args and x are stored in the local variable array in this frame. On line 25, we create an instance of Person passing in the String literal, Joe Bloggs, and the integer literal, 23. Any String literal is itself a String object and is stored on the heap. In addition, as it is a String literal, this String object is stored in a special area of the heap called the String Pool (also known as the String Constant Pool).

The instance variable name inside the Person object resides on the heap and is a String type; that is, it is a reference variable, and it refers to the Joe Bloggs object in the String pool. The other instance variable in Person, namely age, is a primitive, and its value of 23 is stored directly inside the object on the heap. However, the reference to the Person object, joeBloggs, is stored on the stack, in the frame for the main() method.

On line 26 in Figure 2.3, we output the local variable, x, which outputs 0 to the standard output device (typically the screen). Line 27 is then executed, as shown in Figure 2.4. First, the println() method from PrintStream (out is of type PrintStream) causes a frame to be pushed onto the stack. In order to simplify the diagram, we have not gone into any detail in that stack frame. Before println() can complete execution, joeBloggs.toString() must first be executed.

As the toString() method in Person has now been invoked/called, a new frame for toString() is pushed onto the stack on top of the println() frame. Next, toString() builds up a local String variable named decoratedName using String literals and the instance variables.

As you are probably aware, if you have a String instance on the left or the right of a + operator, the overall operation becomes a String append and you end up with a String result.

These String literals are stored in the String Pool. The final String result is My name is Joe Bloggs and I am 23 years old, which is assigned to the local variable, decoratedName. This String is returned from toString() back to the println() statement on line 27 that called it. The returned String is then echoed to the screen.

That concludes our section on storing objects on the heap. Now we will turn our attention to areas that can cause subtle issues in your code. However, now that we have separated the reference from the object, these issues will be much easier to understand and fix.

You have been reading a chapter from
Java Memory Management
Published in: Nov 2022
Publisher: Packt
ISBN-13: 9781801812856
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime