How Close Is Java to the Linux Kernel? Exploring JVM, pthreads, and clone
This article walks through the path from a Java program to the Linux kernel, detailing how the JVM is launched, how libjvm.so is loaded, how pthread_create maps to the clone system call, and what kernel resources are shared by Java threads.
How Close Is Java to the Kernel?
In previous posts we covered cgroup internals; this article looks at the whole chain from a Java application to the Linux kernel for developers who are not kernel programmers.
Test Environment
JVM Entry Point
The JVM starts in java.base/share/native/launcher/main.c:
JNIEXPORT int main(int argc, char **argv) {
// ... many argument handling lines omitted ...
return JLI_Launch(margc, margv, jargc, (const char**)jargv,
0, NULL,
VERSION_STRING,
DOT_VERSION,
(const_progname != NULL) ? const_progname : *margv,
(const_launcher != NULL) ? const_launcher : *margv,
jargc > 0,
const_cpwildcard, const_javaw, 0);
} JLI_Launchperforms three key actions:
Calls CreateExecutionEnvironment to locate the JVM library (e.g., /usr/lib/jvm/java-14-openjdk-amd64/lib/server/libjvm.so).
Calls LoadJavaVM to load libjvm.so and obtain the JNI entry points.
Invokes JVMInit to start the Java program.
Loading libjvm.so
jboolean LoadJavaVM(const char *jvmpath, InvocationFunctions *ifn) {
void *libjvm;
libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL);
ifn->CreateJavaVM = (CreateJavaVM_t) dlsym(libjvm, "JNI_CreateJavaVM");
ifn->GetDefaultJavaVMInitArgs = (GetDefaultJavaVMInitArgs_t) dlsym(libjvm, "JNI_GetDefaultJavaVMInitArgs");
ifn->GetCreatedJavaVMs = (GetCreatedJavaVMs_t) dlsym(libjvm, "JNI_GetCreatedJavaVMs");
return JNI_TRUE;
}Verification can be done with:
objdump -D /usr/lib/jvm/java-14-openjdk-amd64/lib/server/libjvm.so \
| grep -E "CreateJavaVM|GetDefaultJavaVMInitArgs|GetCreatedJavaVMs" \
| grep ":$"JVM Initialization Flow
The launch sequence continues with JVMInit, which calls JavaMain:
int CallJavaMainInNewThread(jlong stack_size, void* args) {
int rslt;
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
if (stack_size > 0) {
pthread_attr_setstacksize(&attr, stack_size);
}
pthread_attr_setguardsize(&attr, 0);
if (pthread_create(&tid, &attr, ThreadJavaMain, args) == 0) {
void* tmp;
pthread_join(tid, &tmp);
rslt = (int)(intptr_t)tmp;
} else {
rslt = JavaMain(args);
}
pthread_attr_destroy(&attr);
return rslt;
}Inside JavaMain the JVM loads the target class and invokes its main method:
int JavaMain(void* _args) {
JavaMainArgs *args = (JavaMainArgs *)_args;
// ... initialize VM ...
if (!InitializeJVM(&vm, &env, &ifn)) {
JLI_ReportErrorMessage(JVM_ERROR1);
exit(1);
}
jclass mainClass = LoadMainClass(env, mode, what);
jmethodID mainID = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V");
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
// ... error handling ...
}From pthread_create to the clone System Call
Java threads are created via pthread_create, which ultimately invokes the Linux clone system call. The relevant clone flags are:
Key flags such as CLONE_VM, CLONE_FS, CLONE_FILES, and CLONE_THREAD make the new thread share the address space, file descriptors, and other resources with its creator, effectively making it a lightweight process.
Steps of the clone Wrapper
Assign the user‑space stack address to ecx.
Store the function pointer ( &start_thread) and a zero guard on the new stack.
Save the caller’s registers on the stack.
Prepare the system‑call arguments, placing clone_flags in ebx and the syscall number in eax.
Issue the int 0x80 interrupt to enter the kernel.
Both parent and child continue execution; the child sees a return value of 0 and jumps to thread_start, which eventually calls the user‑provided start_routine.
Conclusion
The path from a Java program to the kernel is short: Java → JVM (libjvm.so) → glibc’s pthread_create → clone system call → kernel thread creation. Understanding this chain helps application and system engineers navigate Linux kernel code without diving deep into kernel internals.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
