How does JVM map a Java thread to a native thread?

I’ve been recently studying about programming language design on multicore platforms. To design such a language, studying other platforms such as Java or C++ helps understand concepts better. There are a few questions interesting to have answers for:

  1. How does JVM maps a Java thread to a native thread to be executed in the underlying operating system?
  2. How is language design and implementation affected by thread scheduling and management in accordance with the operating system?

Starting with the first one, Java threads actually have two faces. One is the one that is seen in the Java programming language and used by the programmer. The other is the native implementation that is provided by Java language and managed by JVM. Java introduces JNI [1]: Java Native Interface. Through JNI, a programmer can have a class that is partly written in Java and partly written in some other language such as C++. JNI is used to implement parts of Java Thread class in C++. This implementation declares the methods and services required in Java Thread features but not implemented in Java itself.

While JNI brings in some of the implementation of Java Thread into C++, source implementation of JDK shows that there is also an abstraction of threads that are completely written in C++ providing the functionality required to work with threads. This abstraction is used by a JVM instance. As JVM is implemented in a platform-specific approach, each platform provides a set of API and libraries to work with OS threads.Thus, the abstraction is used by JVM to bridge the Java Thread model to an OS-specific model to be executed. For the same reason, a JVM implementation comes with a platform-specific source that will handle different settings regarding CPU and OS variations. For instance, there is a `linux_os_cpu.cpp` source file in the JVM that denotes that implementation is specific for a Linux-based operating system dealing with CPU requirements.

So, let’s the configuration to a multicore processor on a Linux-based operating system having a relevant JVM implementation. On one level, a JVM instance will handle a Java Thread and converts it into an OS native thread that will be executed. On another level, Linux provides a high-level abstraction of threads to be used by different applications. The modern library that handles this is called Native POSIX Thread Library (NPTL) [2] that is a C++ library to enable the Linux kernel to run threads that are written based on POSIX Thread Standard (PThreads) [3]. Thus, JVM implementation actually takes advantage of a PThreads implementation called NPTL and maps a Java Thread to an instance of thread that will be understandable and executable by the Linux kernel.

Speaking about the kernel, the Linux kernel understands some scheduled entity called task that will be managed and executed according to the algorithm  used in the kernel. So here, we have a problem of mapping an application thread to a kernel schedulable entity. There are three models that can be discussed: kernel-level threading (1:1), user-level threading (N:1), and hybrid threading (N:M) [4]. In kernel-level threading, each thread in the application and user space is converted to exactly one scheduled entity in the kernel space. In user-level threading, all threads in an application are mapped to only one thread in the kernel space. Hybrid threading is a mixture of both. Browsing through OS implementation history, it reveals that most implementations have converged to 1:1 model since it utilizes the processing power and relieves the task of scheduling from the languages and libraries. Also, it is argued that implementing N:M model will be costly while complex  and also usually operating system implementation will provide better and optimized services such as scheduling and context switching. Linux kernel uses a 1:1 mapping model. Since JVM only maps a Java Thread to a native OS thread, JVM is also following 1:1 model. So, if a Java program is written in a way to utilize several processors, it is guarantees that the Linux kernel will maximize the simultaneous use of different core as much as possible. Since Java 1.2+, JVM put aside the concepts of Green Threads [5] and used instead the native thread features to map Java Thread to native operating system threads.

Through the discussion, we mentioned the scheduling of kernel entities for execution (tasks). As of Linux kernel 2.6.23+, they have implemented a scheduler algorithm called Completely Fair Scheduler (CFS) [6]. CFS will optimize the most important scheduling workload to O(1) [7]. Also, it has a feature that will check for load balancing the work load among the processors and redistributes the work if necessary [7].So, this way the reason not to go to N:M model gets even more strengthen as the Linux kernel features promise more while efficient. On the other hand, when a process is created it will hold a number of threads of execution. Each process, and its child threads, will be given a Processor Affinity [8] that is a simple map to show that how much this process (thread) is likely to be executed on a specific core among the processors. This is done to minimize the costs in case some thread is being reactivated in the same core and some of its data is present nearby.

To conclude, as the native operating systems on multicore is doing pretty well in terms of abstraction and performance, it is worth to consider such facts when designing and implementing a language that aims the challenges of programming on multicore platforms. Since the mapping of application threads to kernel threads are handled in a neat way, maybe it would not be wise to meddle with the such problems in the language design.

Accordingly, I also posted two questions ([9] and [10]) on and received good answers.

  1. [1]:
  2. [2]:
  3. [3]:
  4. [4]:
  5. [5]:
  6. [6]:
  7. [7]:
  8. [8]:
  9. [9]:
  10. [10]: