Search code examples
javamultithreadingjvmthread-priority

Do Linux JVMs actually implement Thread priorities?


Wrote a quick Java proggy to spawn 10 threads with each priority and calculate pi (4*atan(1) method) with BigDecimals 500,000 times each, join on each thread and report the elapsed time for run method. Yeah, prob'ly not the best example, but keeping it basic.

I'm aware of Bug4813310

It is non-trivial to do in C, but can we assume that native priorities are never set on Linux JVMs?

$uname -r && grep bogomips /proc/cpuinfo
2.4.33.3
bogomips        : 4312.26
$java -version 2>&1 |head -1
Java version "1.6.0_01"
$javac T.java && java -Xmx32m -XX:+UseThreadPriorities T
1:3112
2:2636
3:2662
4:3118
5:2870
6:3319
7:3412
8:3304
9:3299
10:3069

Looks like not much of a deviation that one would expect! That was on a small virtual Linux machine. Maybe just Sun's? We shall try IBM J9 VM:

1:4091
2:4142
3:3957
4:3905
5:3984
6:3985
7:4130
8:4055
9:3752
10:4071

The gross numbers look pretty good in comparison, but there is no scale to the numbers from a thread priority perspective.

Let's try 500k iterations on a 2.6 kernel with an older Sun JVM, one that is constantly loaded with load averages rarely below 7:

$uname -r && grep bogomips /proc/cpuinfo
2.6.9-67.ELsmp
bogomips        : 3992.93
bogomips        : 3990.00
$java -version 2>&1 |head -1
java version "1.4.2_14"
$javac T.java && java -Xmx32m -XX:+UseThreadPriorities T
1:63200
2:64388
3:62532
4:58529
5:62292
6:64872
7:64885
8:64584
9:61653
10:61575

Let's try IBM's J9 on a real slab just with 2.6 kernel and since a bigger system I'll increase iterations to 2,000,000.

$uname -r && grep bogomips /proc/cpuinfo
2.6.9-78.ELsmp
bogomips        : 5989.03
bogomips        : 5985.03
bogomips        : 5985.01
bogomips        : 5985.02
bogomips        : 5984.99
bogomips        : 5985.02
bogomips        : 5984.99
bogomips        : 5985.02
$java -Xmx32m T # this is the IBM J9
1:1718
2:1569
3:1989
4:1897
5:1839
6:1688
7:1634
8:1552
9:2027
10:1522

Some great times, but still no apparent thread/process priorities.

Let's try a Windows box. I know that Windows has a fairly aggressive thread priority scheme. Anything above normal anecdotaly consumes much more. As such, let's move to 900,000 iterations in each thread:

C:\>java -version
java version "1.6.0_11"
C:\>java -Xmx32m T
1:12578
2:12625
3:11469
4:11453
5:10781
6:8937
7:10516
8:8406
9:9953
10:7391

Very much what we're looking for, no?

So Linux JVM's apparently don't have thread priority? I understand that you can't really renice to a lower nice level in C, but I would assume JVM engineers would have figured out how to keep a low-niced dispatcher of sorts.


Solution

  • From the JDK 8 source code, a comment reads:

    ////////////////////////////////////////////////////////////////////////////////
    // thread priority support
    
    // Note: Normal Linux applications are run with SCHED_OTHER policy. SCHED_OTHER
    // only supports dynamic priority, static priority must be zero. For real-time
    // applications, Linux supports SCHED_RR which allows static priority (1-99).
    // However, for large multi-threaded applications, SCHED_RR is not only slower
    // than SCHED_OTHER, but also very unstable (my volano tests hang hard 4 out
    // of 5 runs - Sep 2005).
    //
    // The following code actually changes the niceness of kernel-thread/LWP. It
    // has an assumption that setpriority() only modifies one kernel-thread/LWP,
    // not the entire user process, and user level threads are 1:1 mapped to kernel
    // threads. It has always been the case, but could change in the future. For
    // this reason, the code should not be used as default (ThreadPriorityPolicy=0).
    // It is only used when ThreadPriorityPolicy=1 and requires root privilege.
    

    ...

    Later on, we see:

    static int prio_init() {
      if (ThreadPriorityPolicy == 1) {
        // Only root can raise thread priority. Don't allow ThreadPriorityPolicy=1
        // if effective uid is not root. Perhaps, a more elegant way of doing
        // this is to test CAP_SYS_NICE capability, but that will require libcap.so
        if (geteuid() != 0) {
          if (!FLAG_IS_DEFAULT(ThreadPriorityPolicy)) {
            warning("-XX:ThreadPriorityPolicy requires root privilege on Linux");
          }
    
          ThreadPriorityPolicy = 0;
        }
      }
    
      if (UseCriticalJavaThreadPriority) {
        os::java_to_os_priority[MaxPriority] = os::java_to_os_priority[CriticalPriority];
      }
    
      return 0;
    }
    

    ...

    And subsequently:

    OSReturn os::set_native_priority(Thread* thread, int newpri) {
      if ( !UseThreadPriorities || ThreadPriorityPolicy == 0 ) return OS_OK;
    
      int ret = setpriority(PRIO_PROCESS, thread->osthread()->thread_id(), newpri);
      return (ret == 0) ? OS_OK : OS_ERR;
    }
    

    So! At least on Sun Java, on Linux, you won't see thread priorities unless you have done -XX:ThreadPriorityPolicy and that seems to require root.