Live data from Hacker News

JVM Anatomy Park

shipilev.net

21–30 of 33 posts

Re: JVM Anatomy Park

#21

Regarding the question in the 1st topic: ( https://shipilev.net/jvm-anatomy-park/1-lock-coarsening-for-... ) " for (...) { synchronized (obj) { // something } } …​could it optimize into this? synchronized (this) { for (...) { The answer would be no in general, I think, since it is unsafe. Moving the entire loop into the synchronized block would make the statement marked XXX above, i.e. "for (...)" execute inside the…

Neither of those optimizations looks safe to me. Couldn't they potentially introduce deadlocks?

Re: JVM Anatomy Park

#22
post #17

This is of no real consequence except curiosity, but I'm at a loss to understand what "park" means in the title. I don't even know what sense of the word is intended. Is it "park" like a city or amusement park that you visit, and the idea is that you enjoy a variety of different activities while you're there? Is it "park" like a ballpark, and the analogy is that sporting events take place there and each one of these…

Maybe it is a reference to the Rick and Morty episode "Anatomy Park". http://rickandmorty.wikia.com/wiki/Anatomy_Park_(episode)

Oh, that has to be it! So it's just a reference I didn't understand.

Re: JVM Anatomy Park

#23

Regarding the question in the 1st topic: ( https://shipilev.net/jvm-anatomy-park/1-lock-coarsening-for-... ) " for (...) { synchronized (obj) { // something } } …​could it optimize into this? synchronized (this) { for (...) { The answer would be no in general, I think, since it is unsafe. Moving the entire loop into the synchronized block would make the statement marked XXX above, i.e. "for (...)" execute inside the…

Neither of those optimizations looks safe to me. Couldn't they potentially introduce deadlocks?

I had the same reaction, but now I'm starting to suspect it actually is safe, for a reason that's not immediately obvious.

Consider the unoptimized version. It's possible that after you release the lock, another thread will take it. But I'm pretty confident there is no guarantee of this, and your own thread might immediately take the lock back. (Probably often, especially if there's just one CPU.) If taking the lock back immediately causes a deadlock, then your code is already broken as written.

So you already must account for the possibility that nothing changes between the time you release the lock and take it again. All the optimization does is take away the _other_ possibility, which was something you couldn't rely on anyway.

Re: JVM Anatomy Park

#24

Earlier quoted context omitted.

Neither of those optimizations looks safe to me. Couldn't they potentially introduce deadlocks?

I had the same reaction, but now I'm starting to suspect it actually is safe, for a reason that's not immediately obvious. Consider the unoptimized version. It's possible that after you release the lock, another thread will take it. But I'm pretty confident there is no guarantee of this, and your own thread might immediately take the lock back. (Probably often, especially if there's just one CPU.) If taking the lock…

Yes, you're right. The definition of an optimization being ``safe'' is tricky in the presence of non-determinism.

The set of possible executions of a correctly optimized program should be a subset of---and specifically, need not be necessarily equal to---the possible executions of the original unoptimized version.

Re: JVM Anatomy Park

#25

Regarding the question in the 1st topic: ( https://shipilev.net/jvm-anatomy-park/1-lock-coarsening-for-... ) " for (...) { synchronized (obj) { // something } } …​could it optimize into this? synchronized (this) { for (...) { The answer would be no in general, I think, since it is unsafe. Moving the entire loop into the synchronized block would make the statement marked XXX above, i.e. "for (...)" execute inside the…

> Moving the entire loop into the synchronized block would make the statement marked XXX above, i.e. "for (...)" execute inside the synchronized block, which has the potential to change the semantics (for example, the statement may include an rpc, and we don't want to make that rpc under the lock). That's not change in semantics. The JVM doesn't provide any guarantees about parallelism or scheduling semantics in the…

>And it's unsafe? For what definition of safety?

Let me try to answer with an example. Let us say, we have original code like this:

  synchronized(this) {
   a();
   b();
 }
 c();
 synchronized(this) {
   d();
   e();
 }
It would be unsafe (in general) to transform the above code to

  synchronized(this) {
    a();
    b();
    c();
    d();
    e();
  }
Simply because Compiler does not know (again, in general) what may happen during the execution of c(). However, the following transformation is safe (the timing behavior changes, but as you point out, that is not a guarantee programmers should expect).

  synchronized(this) {
    p();
    q();
  }
  synchronized(this) {
    r();
    s();
  }
to

  synchronized(this) {
    p();
    q();
    r();
    s();
  }
since there is nothing happening between the two synchronized sections.

For the for loop, an example of code where pulling the synchronized statement out of the loop is problematic:

  for(a = AcquireLock(), c = 0; c 
I don't think it is safe to transform to

  a = AcquireLock();
  synchronized(this) {
  for(c = 0; c 
Perhaps you (and the original blog post) assume we are only talking about movement of code after ensuring that such movement is safe, but it was not clear from the document.

Re: JVM Anatomy Park

#26

Earlier quoted context omitted.

> Moving the entire loop into the synchronized block would make the statement marked XXX above, i.e. "for (...)" execute inside the synchronized block, which has the potential to change the semantics (for example, the statement may include an rpc, and we don't want to make that rpc under the lock). That's not change in semantics. The JVM doesn't provide any guarantees about parallelism or scheduling semantics in the…

>And it's unsafe? For what definition of safety? Let me try to answer with an example. Let us say, we have original code like this: synchronized(this) { a(); b(); } c(); synchronized(this) { d(); e(); } It would be unsafe (in general) to transform the above code to synchronized(this) { a(); b(); c(); d(); e(); } Simply because Compiler does not know (again, in general) what may happen during the execution of c(). How…

Just looking at your final example, I can't understand how you can say that is unsafe. How would anyone be able to tell the difference between the two? I think anything you tell me I'm just going to be able to answer 'but Java never guaranteed you that in the first place'. If nobody can tell the difference then how can it be unsafe?

Re: JVM Anatomy Park

#27

Earlier quoted context omitted.

>And it's unsafe? For what definition of safety? Let me try to answer with an example. Let us say, we have original code like this: synchronized(this) { a(); b(); } c(); synchronized(this) { d(); e(); } It would be unsafe (in general) to transform the above code to synchronized(this) { a(); b(); c(); d(); e(); } Simply because Compiler does not know (again, in general) what may happen during the execution of c(). How…

Just looking at your final example, I can't understand how you can say that is unsafe. How would anyone be able to tell the difference between the two? I think anything you tell me I'm just going to be able to answer 'but Java never guaranteed you that in the first place'. If nobody can tell the difference then how can it be unsafe?

>Just looking at your final example, I can't understand how you can say that is unsafe

Let me change the example a bit. Say we have two locks aL and bL, that we must always acquire in the order aL first and then bL.

Following the rule, say we write code like this:

  import java.util.concurrent.locks.ReentrantLock;
  class X {
    private static ReentrantLock aL = new ReentrantLock();
    private static ReentrantLock bL = new ReentrantLock();
    static int x = 0;
    static int c = 0;
    static public void main(String[] args) {
	for(aL.lock(); c 
If I understood it right, the blog post was asking a question whether JVM can transform this to:

  import java.util.concurrent.locks.ReentrantLock;
  class X {
    private static ReentrantLock aL = new ReentrantLock();
    private static ReentrantLock bL = new ReentrantLock();
    static int x = 0;
    static int c = 0;
    static public void main(String[] args) {
      synchronized(bL) {
        for(aL.lock(); c 
Since the locks are now acquired in a different order, does that not qualify as observable behavior?

Re: JVM Anatomy Park

#28

Earlier quoted context omitted.

Just looking at your final example, I can't understand how you can say that is unsafe. How would anyone be able to tell the difference between the two? I think anything you tell me I'm just going to be able to answer 'but Java never guaranteed you that in the first place'. If nobody can tell the difference then how can it be unsafe?

>Just looking at your final example, I can't understand how you can say that is unsafe Let me change the example a bit. Say we have two locks aL and bL, that we must always acquire in the order aL first and then bL. Following the rule, say we write code like this: import java.util.concurrent.locks.ReentrantLock; class X { private static ReentrantLock aL = new ReentrantLock(); private static ReentrantLock bL = new Ree…

But that's just a different example to the one you gave before. In your previous example acquiring the explicit lock always came before the start of synchronised block, both before and after the rewrite. You've changed it here so it's a different question.

Re: JVM Anatomy Park

#29

Earlier quoted context omitted.

>Just looking at your final example, I can't understand how you can say that is unsafe Let me change the example a bit. Say we have two locks aL and bL, that we must always acquire in the order aL first and then bL. Following the rule, say we write code like this: import java.util.concurrent.locks.ReentrantLock; class X { private static ReentrantLock aL = new ReentrantLock(); private static ReentrantLock bL = new Ree…

But that's just a different example to the one you gave before. In your previous example acquiring the explicit lock always came before the start of synchronised block, both before and after the rewrite. You've changed it here so it's a different question.

Sorry, I meant to write:

  synchronized(this) {
    a = AcquireLock();
    for(c = 0; c 
which is inline with what the blog post was proposing.

To repeat the blog is a question:

  for (...) {
    synchronized (obj) {
      // something
    }
  }
…​could it optimize into this?

  synchronized (this) {
    for (...) {
       // something
    }
  }
My answer to that is in general, no.

Re: JVM Anatomy Park

#30

Earlier quoted context omitted.

But that's just a different example to the one you gave before. In your previous example acquiring the explicit lock always came before the start of synchronised block, both before and after the rewrite. You've changed it here so it's a different question.

Sorry, I meant to write: synchronized(this) { a = AcquireLock(); for(c = 0; c which is inline with what the blog post was proposing. To repeat the blog is a question: for (...) { synchronized (obj) { // something } } …​could it optimize into this? synchronized (this) { for (...) { // something } } My answer to that is in general, no.

You mean because ... could be code that can detect whether or not the monitor is held?

Yes, but I think it's an assumption so obvious as to be not worth stating that the author means as long as ... does not do that.

Post reply on HN