Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does "eliminated" mean in a Java stack trace?

Tags:

I'm looking at a thread dump of my Java application, and have noticed that sometimes instead of showing "locked", I see the keyword "eliminated", as seen below:

"Worker [4]" prio=10 tid=0x00007fb1262d8800 nid=0x89a0 in Object.wait() [0x00007fb15b147000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:503)
        at com.myapp.common.util.WaitableQueue.getAll(WaitableQueue.java:152)
        - eliminated <0x00000004d0d28e18> (a com.myapp.common.util.balq.SingleQueueQController$_WorkerQueue)
        at com.myapp.common.util.balq.SingleQueueQController$_WorkerQueue.getAll(SingleQueueQController.java:3527)
        - locked <0x00000004d0d28e18> (a com.myapp.common.util.balq.SingleQueueQController$_WorkerQueue)
        at com.myapp.common.util.AbstractWorker.read(AbstractWorker.java:678)
        at com.myapp.common.util.AbstractWorker.runBulk(AbstractWorker.java:541)
        at com.myapp.common.util.AbstractWorker.run(AbstractWorker.java:343)

Surprisingly, I can't find anything on Google about this. What is the difference between the "locked" and "eliminated" keywords?

like image 574
zedix Avatar asked Feb 07 '14 15:02

zedix


People also ask

What does a stack trace do in Java?

Simply put, a stack trace is a representation of a call stack at a certain point in time, with each element representing a method invocation. The stack trace contains all invocations from the start of a thread until the point it's generated. This is usually a position at which an exception takes place.

Can stackoverflow error be caught?

StackOverflowError is a runtime error which points to serious problems that cannot be caught by an application. The java. lang. StackOverflowError indicates that the application stack is exhausted and is usually caused by deep or infinite recursion.

Can Java handle stack overflow error?

Stack overflow means, that you have no room to store local variables and return adresses. If your jvm does some form of compiling, you have the stackoverflow in the jvm as well and that means, you can't handle it or catch it. The jvm has to terminate.


1 Answers

It represents a redundant lock that has been removed in the bytecode.

I always find the source code a good place to start with things like this. A review of hotspot/src/share/vm/opto/callnode.cpp from the openJDK had the following interesting comments:

// Redundant lock elimination
//
// There are various patterns of locking where we release and
// immediately reacquire a lock in a piece of code where no operations 
// occur in between that would be observable.  In those cases we can
// skip releasing and reacquiring the lock without violating any
// fairness requirements.  Doing this around a loop could cause a lock
// to be held for a very long time so we concentrate on non-looping
// control flow.  We also require that the operations are fully 
// redundant meaning that we don't introduce new lock operations on
// some paths so to be able to eliminate it on others ala PRE.  This
// would probably require some more extensive graph manipulation to
// guarantee that the memory edges were all handled correctly.
//
// Assuming p is a simple predicate which can't trap in any way and s
// is a synchronized method consider this code:
//
//   s();
//   if (p)
//     s();
//   else
//     s();
//   s();
//
// 1. The unlocks of the first call to s can be eliminated if the
// locks inside the then and else branches are eliminated.
//
// 2. The unlocks of the then and else branches can be eliminated if
// the lock of the final call to s is eliminated.
//
// Either of these cases subsumes the simple case of sequential control flow

So from the above, it would seem (at least in openJDK) that eliminated means the lock is maintained by the JVM through one or more set of release/acquire instructions.

Reviewing the javaVFrame::print_lock_info_on() in hotspot/src/share/vm/runtime/vframe.cpp shows where the check and output occurs:

// Print out all monitors that we have locked or are trying to lock
GrowableArray<MonitorInfo*>* mons = monitors();
if (!mons->is_empty()) {
  bool found_first_monitor = false;
  for (int index = (mons->length()-1); index >= 0; index--) {
    MonitorInfo* monitor = mons->at(index);
    if (monitor->eliminated() && is_compiled_frame()) { // Eliminated in compiled code
      if (monitor->owner_is_scalar_replaced()) {
        Klass* k = Klass::cast(monitor->owner_klass());
        st->print("\t- eliminated <owner is scalar replaced> (a %s)", k->external_name());
      } else {
        oop obj = monitor->owner();
        if (obj != NULL) {
          print_locked_object_class_name(st, obj, "eliminated");
        }
      }
      continue;

Further comments including the one above also allude to replacing the lock and unlock instructions with NOP.

I read the document Dirk referred to regarding lock elision and it seems to be Lock Coarsening rather than Elision:

Another optimization that can be used to reduce the cost of locking is lock coarsening. Lock coarsening is the process of merging adjacent synchronized blocks that use the same lock object. If the compiler cannot eliminate the locking using lock elision, it may be able to reduce the overhead by using lock coarsening.

But to be honest the difference is very subtle and the end effect is pretty much the same - you eliminate unnecessary locks and unlocks.

like image 91
GeekyDeaks Avatar answered Sep 21 '22 23:09

GeekyDeaks