Please pay attention to the definition "Hotspot Virtual Machine" in this article, and clearly stated in the virtual machine specification: "All implementation details not explicitly described in the virtual machine specification, virtual Machine designers should not be creative. Designers can decide completely the internal details of virtual machines that are not described in all specifications. For example: how to deploy the memory in the Data zone during running, and which garbage collection algorithm is used ". Therefore, the content in this article (all articles in the memory) will involve the implementation of the Virtual Machine "autonomous decision". Our discussion will be carried out within the scope of the hotspot VM. At the same time, I also assume that the reader has understood the JVM public memory model defined in the VM specification, such as the basic knowledge of the runtime data area and stack frame structure. If the reader has doubts about this content, read the Java Virtual Machine specification (javase)
7 Editon) [1] Chapter 2nd or chapter 2nd and 3 of "deep understanding of Java Virtual Machine: JVM advanced features and Best Practices" [2.
Object Creation
Java is an object-oriented programming language. During Java program running, objects are created every moment. At the language level, creating an object (except clone and deserialization) is just a New Keyword. in virtual machines, objects (the objects discussed in this article are limited to common Java objects, what is the process of creating an array or a class object?
When the VM encounters a new command, it first checks whether the parameter of this command can locate a Class symbol reference in the constant pool, check whether the class referenced by this symbol has been loaded, parsed, and initialized. If no, the corresponding class loading process must be executed first.
After the class is loaded, the VM allocates memory for the new object. The size of the memory required by the object can be fully determined after the class is loaded (how to determine the layout of the object memory in the next section ), the task of allocating space for objects is equivalent to dividing a fixed memory size from the Java heap? Assuming that the memory in the Java heap is absolutely regular, all used memory is put on one side, idle memory is put on the other side, and a pointer is put in the middle as the indicator of the demarcation point, the allocated memory is just to move the pointer to the idle space to a distance equal to the object size. This allocation method is called "pointer collision" (bump the pointer ). If the memory in the Java heap is not regular, and the used memory and idle memory are intertwined, there is no way to perform a pointer collision. The virtual machine must maintain a list, record which memory blocks are available. When allocated, find a sufficient space in the list and divide it into the object instance, and update the records in the list, this allocation method is called "Free List" (free
List ). The distribution method is determined by whether the Java heap is regular, and whether the Java heap is regular is determined by whether the garbage collector is compressed. Therefore, when using collectors with compact processes such as serial and parnew, the allocation algorithm adopted by the system is Pointer collision, and CMS is used as the Mark-sweep algorithm-based collector, the CMS collector can sort the memory by usecmscompactatfullcollection or cmsfullgcsbeforecompaction.
In addition to how to divide the available space, another issue to be considered is that objects are frequently created in virtual machines, even if only one pointer is pointed, in the case of concurrency, It is not thread-safe. It may be that the memory is being allocated to object A. the pointer has not been modified yet, and object B uses the original pointer to allocate memory at the same time. There are two solutions to solve this problem. One is to synchronize the actions of allocating memory space-in fact, the virtual machine uses CAs with failure retry to ensure the atomicity of update operations; the other is to divide the memory allocation action into different spaces by thread, that is, each thread allocates a small block of memory in the Java heap in advance, called the local thread allocation buffer (tlab
, Thread local allocation buffer), which thread needs to allocate memory, is allocated on the tlab of which thread. Synchronization locks are required only when tlab is used up and new tlab is allocated. Whether the virtual machine uses tlab can be set through the-XX: +/-usetlab parameter.
After the memory allocation is complete, the virtual machine needs to initialize the allocated memory space to a zero value (excluding the object header). If tlab is used, this work can also be done in advance when tlab is assigned. This step ensures that the instance field of the object can be directly used in Java code without assigning an initial value. The program can access the zero value corresponding to the Data Type of these fields.
Next, the virtual machine needs to make necessary settings for the object, for example, the instance of the class of the object, how to find the metadata information of the class, the hash code of the object, the GC age of the object, and so on. This information is stored in the object header. Depending on the current running status of the virtual machine, such as whether to enable the biased lock, the object header will have different settings. The specific content of the object header will be detailed in the next section.
After the above work is completed, a new object has been generated from the virtual machine perspective. However, from the perspective of the Java program, the object creation has just begun -- <init> the method has not been executed yet, And all fields are zero. Therefore, in general (determined by whether the bytecode follows the invokespecial command), the new command is followed by the <init> method to initialize the object according to the programmer's wishes, such a truly available object is generated completely.
The following code is the hotspot Virtual Machine bytecodeinterpreter. code snippets in CPP (This interpreter is rarely used in practice. Most platforms use the template interpreter. When code is executed through the JIT compiler, the time difference is even greater. However, this code is used to understand how hot spot works ).
Code List 1: Hotspot interpreter code snippet
// Make sure that the constant pool stores the interpreted class if (! Constants-> tag_at (index ). is_unresolved_klass () {// assertion ensures that klassoop and instanceklassoop (described in the next section) oop entry = (klassoop) * constants-> obj_at_addr (INDEX ); assert (Entry-> is_klass (), "shocould be resolved Klass"); klassoop k_entry = (klassoop) entry; Assert (k_entry-> klass_part ()-> oop_is_instance (), "shoshould be instanceklass"); instanceklass * ik = (instanceklass *) k_entry-> klass_part (); // ensure that the object type has passed the initialization phase if (Ik-> is_initialized () & Ik-> can_be_fastpath_allocated () {// obtain the object length size_t obj_size = Ik-> size_helper (); OOP result = NULL; // record whether all fields of the object need to be set to zero bool need_zero =! Zerotlab; // whether to allocate objects in tlab if (usetlab) {result = (OOP) thread-> tlab (). allocate (obj_size);} If (result = NULL) {need_zero = true; // allocate the object retry: heapword * compare_to = * Universe: heap () directly in Eden () -> top_addr (); heapword * new_top = compare_to + obj_size; // cmpxchg is the CAS command in x86. Here is a C ++ method, which allocates space through CAS, if the concurrency fails, go to retry and try again until the allocation is successful. If (new_top <= * Universe: heap ()-> end_addr () {If (atomic: cmpxchg_ptr (new_t Op, Universe: heap ()-> top_addr (), compare_to )! = Compare_to) {goto retry;} result = (OOP) compare_to;} If (result! = NULL) {// if needed, initialize the zero value for the object if (need_zero) {heapword * to_zero = (heapword *) Result + sizeof (oopdesc)/oopsize; obj_size-= sizeof (oopdesc)/oopsize; If (obj_size> 0) {memset (to_zero, 0, obj_size * heapwordsize) ;}/// biased lock is enabled based on whether or not, set object header information if (usebiasedlocking) {result-> set_mark (Ik-> prototype_header ();} else {result-> set_mark (markoopdesc: Prototype ());} result-> set_klass_gap (0); Result-> set_klass (k_entry); // reference the object to the stack and continue executing the next instruction set_stack_object (result, 0); update_pc_and_tos_and_continue (3, 1 );}}}
Object Memory Layout
In a hot spot virtual machine, the layout of objects stored in memory can be divided into three areas: object header, instance data, and padding ).
The object header of the hotspot Virtual Machine includes two parts. The first part is used to store the data during the running of the object, such as the hashcode) GC generational age, lock status mark, lock held by the thread, biased thread ID, biased timestamp, etc, the data length is 32 bits and 64 bits in 32-bit and 64-bit virtual machines (for the moment, the compression pointer is not considered, it is officially called "Mark word ". The number of runtime data that an object needs to store exceeds the record limit of the 32-bit and 64-Bit Bitmap structures, however, the object header information is an additional storage cost unrelated to the data defined by the object. Considering the space efficiency of the virtual machine, mark word is designed as a non-fixed data structure to store as much information as possible in a very small space. It will reuse its storage space according to the object state. For example, if an object in a 32-bit hot spot virtual machine is not locked
25 bits in 32 bits space of word are used to store hashcode, 4 bits are used to store the age of object generation, 2 bits are used to store the lock flag, and 1 bit is fixed to 0, the following table lists the storage content of objects in other states (lightweight locking, heavyweight locking, GC marking, and bias.
Table 1 hotspot virtual machine object header mark word
Storage content |
Flag Space |
Status |
Object hash code, object generation age |
01 |
Unlocked |
Pointer to lock record |
00 |
Lightweight locking |
Pointer to the heavyweight lock |
10 |
Expansion (heavyweight lock) |
Null, no need to record information |
11 |
GC mark |
Biased towards thread ID, biased towards timestamp, object generational age |
01 |
Bias |
Another part of the object header is the type pointer, which is the pointer to the class metadata of the object. The VM uses this pointer to determine the instance of the class of the object. Not all Virtual Machine implementations must retain the type pointer on the object data. In other words, the metadata information of the object does not have to pass through the object itself. This is discussed in the next section. In addition, if the object is a Java array, a piece of data must be included in the object header to record the length of the array, the virtual machine can determine the size of a Java object through the metadata information of a common Java object, but the size of an array cannot be determined from the metadata of an array.
The following is a code (Note) snippet in markoop. cpp of the hotspot virtual machine. It describes the storage status of markword under 32bits:
// Bit-format of an object header (most significant first, big endian layout below): // // 32 bits: // -------- // hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) // size:32 ------------------------------------------>| (CMS free block) // PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
Next, the instance data is the valid information that the object actually stores. It is also the content of various types of fields defined in the program code, whether inherited from the parent class, or all defined in the subclass must be recorded. The storage order of this part will be affected by the order defined by the VM allocation policy parameters (fieldsallocationstyle) and fields in the Java source code. The default allocation policies for hot spot virtual machines are longs/doubles, ints, shorts/chars, Bytes/booleans, And oops (ordinary object pointers). We can see from the allocation policy that, fields of the same width are always allocated together. If this condition is met, the variables defined in the parent class appear before the subclass. If the compactfields parameter value is true (the default value is true), narrow variables in the subclass may also be inserted into the gap between parent variables.
The third part of alignment filling does not exist and has no special meaning. It only plays a placeholder role. Because the Automatic Memory Management System of the hotspot VM requires that the starting address of an object must be an integer multiple of 8 bytes, that is, the object size must be an integer multiple of 8 bytes. The object header is like a multiple of 8 bytes (1 or 2 times). Therefore, when the data part of the object instance is not aligned, it needs to be filled with alignment.
Object Access and positioning
Objects are created to use objects. Our Java program uses reference data on the stack to operate specific objects on the stack. Since the reference type only specifies a reference pointing to an object in the Java Virtual Machine specification, it does not define how this reference can be used to locate and access the specific location of the object in the heap, the object access method depends on the Virtual Machine implementation. There are two mainstream access methods: handle and direct pointer.
These two object access methods have their own advantages. The biggest advantage of using a handle to access is that the reference stores a stable handle address, when an object is moved (moving objects during garbage collection is a very common behavior), it only changes the instance data pointer in the handle, and the reference itself does not need to be modified.
The biggest advantage of using direct pointers for access is faster, which saves the time overhead of a pointer location. Because objects are frequently accessed in Java, therefore, the small accumulation of such overhead is also a very considerable execution cost. From the object memory layout described in the previous section, we can see that as far as virtual machine hotspot is concerned, it uses the second method for object access. However, in the entire scope of software development, it is also common to use handles for access in various languages and frameworks.
References
This article mainly references the following materials:
- Http://icyfenix.iteye.com/blog/1095132
- Http://rednaxelafx.iteye.com/blog/858009
- Http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html
[1] http://jcp.org/aboutJava/communityprocess/maintenance/jsr924/index3.html
[2] http://icyfenix.iteye.com/blog/1095132