When we write a Java program, it runs inside something called the Java Virtual Machine (JVM). The JVM manages memory, threads, and many other things behind the scenes. Sometimes, we want to look inside the JVM while our program is running just like a doctor uses an X-ray to see what’s happening inside a body. For this, Java provides tools, and one of the most useful ones is JConsole.
JConsole comes with the JDK, and it lets us see:
· How much memory our program is using
· How many threads are running
· Other details about JVM performance
This is especially useful when learning about virtual threads because we want to compare them with platform threads and see the difference in resource usage.
Step 1: Creating Platform Threads
Let’s start by creating 1000 platform threads (the “traditional” Java threads). Each thread will simply sleep for 60 seconds. Sleeping means the thread is doing nothing but waiting like a person sitting idle in a waiting room.
PlatformThreadBulkExample.java
package com.sample.app.virtual.threads.creation; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; public class PlatformThreadBulkExample { public static void main(String[] args) { IntStream.range(0, 1000).forEach(i -> { Thread.ofPlatform().start(() -> { try { TimeUnit.SECONDS.sleep(60); // Sleep for 60 seconds } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Fetching data for request " + i + " in " + Thread.currentThread()); }); }); } }
Here:
· We start 1000 threads.
· Each one just waits for 1 minute.
· This gives us enough time to inspect the JVM with JConsole.
Step 2: Opening JConsole
a. Run the program above.
b. Open a terminal and type: jconsole
c. A window opens showing the running Java processes. Select your program
Go to the Overview tab. You’ll notice:
· Around 1000+ live threads.
· Heap memory usage is around 200 MB.
This shows that platform threads consume a lot of memory and system resources when you create them in bulk.
Step 3: Creating Virtual Threads
Now let’s run the same logic but with virtual threads.
VirtualThreadBulkExample.java
package com.sample.app.virtual.threads.creation; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; public class VirtualThreadBulkExample { public static void main(String[] args) throws InterruptedException { IntStream.range(0, 1000).forEach(i -> { Thread.ofVirtual().start(() -> { try { TimeUnit.SECONDS.sleep(60); // Sleep for 60 seconds } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Fetching data for request " + i + " in " + Thread.currentThread()); }); }); // Main thread waits long enough so we can inspect with JConsole TimeUnit.SECONDS.sleep(120); } }
Now repeat the JConsole steps:
· Open JConsole again and connect to your program (VirtualThreadBulkExample).
· Go to the Overview tab.
You’ll notice:
· Only about 30 threads are shown.
· Heap memory usage is around 40 MB.
Why so few? Because JConsole only shows platform threads (the real OS threads). Virtual threads are lightweight and don’t need one-to-one mapping with platform threads. Instead, they share a smaller pool of platform threads.
Why This Matters in Real-World Applications?
· In real-world web applications, each incoming request often needs a thread.
· With platform threads, you can’t easily handle thousands of simultaneous requests because threads are expensive.
· With virtual threads, you can create millions of concurrent tasks without exhausting memory or CPU.
This makes Java applications more scalable without rewriting your code in complex async style.
Previous Next Home

No comments:
Post a Comment