Reputation: 161
I write multithreaded software but there's something I don't understand about Java.
My understanding is that Java objects share a memory address space. So any number of threads can see and access any number of objects.
If I create threads in the main thread and pass an object in the constructor to each of those threads, the Java interpreter does no marshalling between threads.
How does Java enforce that the memory model of Java programs are thread safe and won't see a partial object construction?
In other words, if a program's constructor is running, what stops the memory model being invalid while the object is being created? Sure I accept that nobody can refer to an object being constructed. But the book keeping of Java must store objects in memory and that needs to be thread safe.
Upvotes: 1
Views: 205
Reputation: 41281
My understanding is that Java objects share a memory address space. So any number of threads can see and access any number of objects.
Yes, they can see properly allocated and constructed objects, that they have a reference to. A Java thread can't just randomly grab an object from heap memory without having a reference to it. The low-level JVM internals don't, by thread-safe design.
If I create threads in the main thread and pass an object in the constructor to each of those threads, the Java interpreter does no marshalling between threads.
Yes, at the Java language level you can leak this
in the constructor. This is a specific way to shoot yourself in the foot, and conversely you can avoid shooting yourself in the foot by not leaking the object reference until the object is ready.
As Basil Bourque points out in a comment, this is another factor for why constructors should generally be short and simple, while complex initialization can benefit from using a builder pattern.
How does Java enforce that the memory model of Java programs are thread safe and won't see a partial object construction?
The low-level heap management machinery is thread-safe, insofar as it allocates blocks of heap memory in a thread-safe manner, and garbage-collects them in a thread-safe manner. Consider the following constructor call being made from two threads at approximately the same time:
someclass x = new someclass();
0 new #1 // Class [someclass]
3 dup
4 invokespecial #4 // Method [someclass].<init>()V
7 areturn
Two threads each execute bytecode new
and get uninitialized objects. Because the low-level heap allocation machinery is thread-safe, the two threads get separate objects. The fact that they're in shared memory space is irrelevant - they're still separate objects.
They call the constructors to initialize their individual objects - and that's still fine. The object reference is only available to java code in two ways - either the assignment to x
after the constructor returns, or if you intentionally leak the reference in the constructor. Even if you do leak a Java reference to the object, the actual low level allocation and initialization of the object's header is essentially complete - the new
bytecode finished running already.
But the book keeping of Java must store objects in memory and that needs to be thread safe.
Yes, and it is. The allocator is thread-safe, the GC is thread-safe, and almost every other low-level object access is only done in response to your Java code trying to do something to an object using a reference that it has.
A fully concurrent GC/allocator can be part of a spec compliant JVM. A single-threaded/stop-the-world GC/allocator can be part of a spec compliant JVM (but that JVM might have poor performance scaling). An allocator that corrupts the heap when two threads create objects at the same time is simply broken, and a JVM built on it is broken.
Upvotes: 7