Introduction
Does the Java object contain:
- fields declared in superclass?
- private fields declared in a superclass?
- methods?
- array elements?
- array length?
- another object (in itself)?
- hash code?
- type (own)?
- name (own)?
Answers to these (and other) questions can be obtained using
the org.openjdk.jol class library, which, in particular, allows us to understand that the object is a memory area:
- containing:
- header (up to 16 bytes), and in it:
- hash code
- type reference
- array length (for array)
- all fields (including private) declared in all superclasses
- or array elements (for an array)
- not containing:
- static variables
- methods
- other objects in yourself
- own name (that is, the object does not have a name)
Training
Here are the results of evaluating the memory of objects of various types by the method from the description of the
java.lang.instrument package (see also
here ). These results allow us to answer most of the questions posed above.
The following steps must be completed:
- Create an agent class containing the premain method:
public static void premain(String, Instrumentation) {...}
- Create an archive containing the agent class and the manifest file with the contents:
Premain-class: --
- Create an executable class for evaluating memory.
- Specify the archive with the "-javaagent" parameter when starting the virtual machine:
java -javaagent:- --
Let's start with a test case. For simplicity we use an unnamed package.
Step 1. Create a trial agent class
import java.lang.instrument.Instrumentation; public class A { public static void premain(String notUsedHere, Instrumentation i) { System.out.println("premain"); } }
We compile:
javac A.java
Step 2. Create the m.txt manifest file containing:
Premain-class: A
ATTENTION: the second line of the file must be EMPTY, NOT CONTAINING SPACES.
Create the A.jar archive:
jar cmf m.txt A.jar A.class
Step 3. Create a trial executable class
public class M { public static void main(String[] notUsedHere) {
We compile:
javac M.java
Step 4. Perform
java -javaagent:A.jar M
Result:
premain main
indicates that the premain method of the agent class was called first, and then the main method of the executable class.
Now create the required agent class:
import java.lang.instrument.Instrumentation; public class A {
and executable class:
class M { public static void main(String[] notUsedHere) { mem("Object", new Object()); } private static void mem(Object o, Object ref) { System.out.println(o + ": " + objectBytesEstimate(ref)); } private static long objectBytesEstimate(Object ref) { if (A.instrumentation() == null) { throw new RuntimeException("Not initialized instrumentation."); } return A.instrumentation().getObjectSize(ref); } }
Method
long getObjectSize(Object --)
returns an ESTIMATION of the size (number of bytes) of memory occupied by the object at the specified link. It must be borne in mind that the resulting estimate may be different for another virtual machine. Values ββfor
jdk-13 will be listed
here .
We carry out:
javac *.java jar cmf m.txt A.jar A.class java -javaagent:A.jar M
and we get the result:
Object: 16
indicating that an EMPTY object of type Object occupies here (BY ASSESSMENT) 16 bytes. Of these, 12 bytes occupies the header, and 4 bytes at the end serve to align the length of the object to the boundary of 8 bytes.
results
Further examples will contain only the code placed in the main method of class M. They should be executed for each example with the commands:
javac M.java java -javaagent:A.jar M
Re-creating A.jar is not necessary.
For example, to get an estimate of the memory size of an object of an arbitrary type without fields, we put the code in the main method:
class C {}; mem("Empty", new C());
The result indicated in the comment shows that an object without fields occupies as many bytes as an object of type Object.
Further, the result of the program:
{class C {int a; } mem(1, new C());}
indicates that each int field takes 4 bytes. I note that here each line is a separate block, which allows you to use the same name for different classes.
Each long field is 8 bytes:
{class C {long a; } mem(1, new C());}
Each boolean-field takes 1 byte (for this VM):
{class C {boolean a; } mem(1, new C());}
Each reference field takes 4 bytes (for this VM):
{class C {Boolean a; } mem(1, new C());}
The String type field also takes 4 bytes, like every reference:
{class C {String a; } mem(" null", new C());}
An array reference field also takes 4 bytes, like every reference one:
{class C {int[] a; } mem("null", new C());}
The subtype object contains each field declared in the superclass, regardless of the access modifier:
{class S { } class C extends S {long a;} mem("0+1", new C());}
The subtype object contains a field declared in the superclass with the same name as in the subclass (the so-called hidden - hidden):
{class S { } class C extends S {long a,b;} mem("0+2", new C());}
A subtype object contains each field declared in each of its superclasses:
class U {private long a; } class S extends U {private long a; } class C extends S { long a; } mem("1+1+1", new C());
Turn to arrays. As you know, an array is a special kind of object whose elements are in the object itself, so the size of the memory occupied by the array grows with the number of elements:
{long[] a = new long[ 0]; mem(" 0", a);}
And for the array of links:
{Long[] a = new Long[ 0]; mem(" 0", a);}
Now, out of curiosity, we compare the sizes of several objects of different types:
mem(" Object", new Object());
The same for different jdk on a 64-bit processor:
jdk1.6.0_45 jdk1.7.0_80 jdk1.8.0_191 jdk-9 jdk-12 jdk-13
----------- ----------- ------------ ------ ------ ---- -
Object: 16 16 16 16 16 16
String: 32 24 24 24 24 24
Exception: 32 32 32 40 40 40
int.class: 104 88 104 112 104 112
int []. class: 584 544 480 112 104 112
Object.class: 600 560 496 112 104 112
System.class: 624 560 496 144 152 160
String.class: 696 640 624 136 128 136
The String size estimate is 24 bytes, although the String class contains many static variables, static and non-static methods. This undoubtedly indicates the absence of static variables and method code in the object. The same is true for an object of any type.
In conclusion - a reminder: all the data on the size of the object are estimated and they can vary to some extent from one execution to another and, of course, for different virtual machines.