This article is participating in the “Java Theme Month – Java Brush questions card”, activity link

The title

Dive into the JVM to analyze synchronized locks


Java object memory structure

In the HotSpot virtual machine, the layout of objects stored in memory can be divided into three areas: object headers, Instance Data, and Padding.

Object Header

MarkWord (Mark field)

The runtime data used to store the object itself, such as HashCode, GC generation age, lock status flags, thread-held locks, bias thread ids, bias timestamps, and so on, are 32 Bits and 64 Bits in 32-bit and 64-bit virtual machines (leaving aside scenarios where compression Pointers are enabled), respectively.

  • New The memory size of an empty object in 32 bytes is 8byte (object header, in the heap) +4byte (object reference address, in the stack) =12byte;

  • New The memory size of an empty object in 64 bytes is 16BYTE (object header, in the heap) +8byte (object reference address, in the stack) =24byte; If compressed pointer mechanism is enabled then 8byte(object header)+4byte link pointer +4byte(reference address in stack).

Mark Word is designed as a flexible data structure to store as much information as possible in a very small space. It reuses its storage space according to the state of the object.

There is no lock

Lock state

Lightweight and biased locks are new additions to Java 6’s optimization of synchronized locks, which we’ll briefly examine later.

  • Here we focus on heavyweight locks, also known as synchronized object locks, with a lock identifier of bit 10, where the pointer points to the starting address of a monitor object (also known as a pipe or monitor lock).

  • Each object has a Monitor associated with it, and the relationship between the object and its Monitor can be implemented in various ways. For example, the monitor can be created and destroyed together with the object or automatically generated when a thread tries to acquire an object lock. However, when a monitor is held by a thread, it is locked.

  • In the Java virtual machine (HotSpot), monitor is implemented by ObjectMonitor and its main data structure is as follows (located in the ObjectMonitor. HPP file of the HotSpot virtual machine source code, implemented in C++)


ObjectMonitor() { _header = NULL; _count = 0; _waiters = 0, _recursions = 0; _object = NULL; _owner = NULL; \_WaitSet = NULL; // Threads in wait state are added to \_WaitSet _WaitSetLock = 0; _Responsible = NULL ; _succ = NULL ; _cxq = NULL ; FreeNext = NULL ; _EntryList = NULL ; // Threads in the lock block state are added to the list. _SpinClock = 0 ; OwnerIsThread = 0 ; }Copy the code

ObjectMonitor has two queues, WaitSet and EntryList, that hold the list of ObjectWaiter objects (each thread waiting for a lock is encapsulated as an ObjectWaiter object). Owner refers to the thread holding the ObjectMonitor object. When multiple threads simultaneously access a piece of synchronized code

  • The EntryList collection is first entered. When the thread obtains the object’s monitor, it enters the Owner area and sets the Owner variable in Monitor to the current thread and the counter count in monitor is incremented by 1.

  • If a thread calls wait(), the currently held monitor is released, the owner variable is restored to null, count is reduced by 1, and the thread enters the WaitSet to be awakened.

  • If the current thread completes, it also releases the monitor(lock) and resets the value of the variable so that another thread can enter to acquire the monitor(lock).

From this point of view, a Monitor object exists in the object header of every Java object (the point of a stored pointer), and synchronized locks are acquired this way, which is why any object in Java can be used as a lock. It is also the reason why notify/notifyAll/ Wait methods exist in the top-level Object.