{"id":5342,"date":"2026-11-14T02:52:51","date_gmt":"2026-11-13T21:22:51","guid":{"rendered":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/"},"modified":"2026-11-14T02:52:51","modified_gmt":"2026-11-13T21:22:51","slug":"benchmarking-java-21-virtual-threads-latency-deep-dive","status":"publish","type":"post","link":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/","title":{"rendered":"Benchmarking Java 21: Virtual Threads Latency Deep Dive"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_89 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-6af83bd2371dc\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-6af83bd2371dc\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#Post-Mortem_Socket_Exhauster_on_Node_04\" >Post-Mortem: Socket Exhauster on Node 04<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#The_Architecture_of_a_Lie_Why_Virtual_Threads_Pin\" >The Architecture of a Lie: Why Virtual Threads Pin<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#Benchmarking_Java_21_Setup_Harness_and_Realistic_Noise\" >Benchmarking Java 21: Setup, Harness, and Realistic Noise<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#The_Asynchronous_IO_Trap_and_Native_Thread_Pools\" >The Asynchronous I\/O Trap and Native Thread Pools<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#Garbage_Collection_Pressure_and_Continuation_Allocation\" >Garbage Collection Pressure and Continuation Allocation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#Flame_Graph_Realities_Where_the_Time_Goes\" >Flame Graph Realities: Where the Time Goes<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#Production_Hardening_Checklist\" >Production Hardening Checklist<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"Post-Mortem_Socket_Exhauster_on_Node_04\"><\/span>Post-Mortem: Socket Exhauster on Node 04<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<pre><code class=\"language-java\">\njava.lang.OutOfMemoryError: Thread creation failed: Operating system resource exhausted\n    at java.base\/java.lang.Thread.start(Thread.java:1539)\n    at java.base\/java.lang.VirtualThread.start(VirtualThread.java:307)\n    at com.fintech.ledger.io.SocketWorkerPool.dispatch(SocketWorkerPool.java:88)\n    at com.fintech.ledger.GatewayServer.lambda$acceptConnections$3(GatewayServer.java:142)\n    at java.base\/jdk.internal.vm.Continuation.enter(Continuation.java:340)\n    ...\nCaused by: java.io.UncheckedIOException: java.net.SocketException: Too many open files\n    at com.fintech.ledger.io.NativeEpollDispatcher.register(NativeEpollDispatcher.java:211)\n<\/code><\/pre>\n<p>It pinned. Hard. After three days of staring at raw kernel-bypass logs on Node 04, watching p99.9 latencies blow past four thousand milliseconds under a modest fifty thousand requests per second, the illusion evaporated. Everyone read the Project Loom papers, swallowed the marketing gloss about lightweight concurrency, and assumed they could swap out thread pools like changing socks. They cannot. When you run <strong>benchmarking java 21<\/strong> workloads under realistic production pressure\u2014combining legacy blocking JDBC drivers, bloated Netty pipelines, and default fork-join schedulers\u2014virtual threads do not magically fix your I\/O bottlenecks. They expose them.<\/p>\n<p>The core thesis here is as blunt as a brick: Virtual threads are a scheduling abstraction, not a hardware upgrade. If your payload relies on synchronized blocks, native JNI calls, or blocking file channels, you are not utilizing cooperative scheduling. You are pinning carrier threads, starving the JVM&#8217;s ForkJoinPool, and creating garbage collection pauses that look less like latency spikes and more like rolling blackouts.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Architecture_of_a_Lie_Why_Virtual_Threads_Pin\"><\/span>The Architecture of a Lie: Why Virtual Threads Pin<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let us look at what actually happens inside the JVM when a virtual thread encounters a blocking operation. Platform threads map one-to-one with OS threads. They cost roughly a megabyte of stack space per thread, forcing developers to use thread pools. Virtual threads decouple this. Millions of virtual threads multiplex onto a small number of carrier threads\u2014typically equal to the number of available CPU cores. When a virtual thread executes blocking I\/O, the JVM mounts the continuation, yields the carrier thread to another virtual thread, and unmounts the stack.<\/p>\n<p>Except when it doesn&#8217;t.<\/p>\n<p>Pinning occurs under two distinct conditions in Java 21:<\/p>\n<ul>\n<li>When code executes inside a <code>synchronized<\/code> block or method.<\/li>\n<li>When native methods or foreign function calls (FFM) are executed on the stack.<\/li>\n<\/ul>\n<p>During pinning, the virtual thread cannot be unmounted from its carrier thread. If your payment gateway code wraps database calls in legacy Hibernate transactions adorned with <code>synchronized<\/code> monitors, your carrier thread stays nailed to that single virtual thread until the database responds. Scale that to twenty thousand concurrent requests hitting a degraded Postgres instance, and your entire carrier pool is exhausted. Your million virtual threads are sitting in the runnable queue, starving while the carrier threads sleep on a network socket.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Benchmarking_Java_21_Setup_Harness_and_Realistic_Noise\"><\/span>Benchmarking Java 21: Setup, Harness, and Realistic Noise<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We built a test harness to isolate this. No cloud-hosted micro-instances with noisy neighbors. We used bare-metal dual AMD EPYC 9654 nodes with 768GB of ECC RAM, running Linux kernel 6.5.0-custom, pinned to isolated NUMA nodes. The load generator ran on a separate physical switch to eliminate packet drops.<\/p>\n<p>We used JMH (Java Microbenchmark Harness) combined with custom async profilers to measure latency distributions at high percentiles. Here is the configuration we used to drive the JMH harness:<\/p>\n<pre><code class=\"language-java\">\n@State(Scope.Benchmark)\n@OutputTimeUnit(TimeUnit.MICROSECONDS)\n@BenchmarkMode(Mode.Throughput)\npublic class VirtualThreadGatewayBenchmark {\n\n    private ExecutorService virtualExecutor;\n    private ExecutorService platformExecutor;\n    private SocketWorkerPool socketPool;\n\n    @Setup(Level.Trial)\n    public void setUp() {\n        this.virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();\n        this.platformExecutor = Executors.newFixedThreadPool(64);\n        this.socketPool = new com.fintech.ledger.io.SocketWorkerPool(\n            8443, \n            Duration.ofMillis(1200)\n        );\n    }\n\n    @Benchmark\n    public void benchmarkVirtualWithBlockingIO(Blackhole bh) throws Exception {\n        virtualExecutor.submit(() -> {\n            byte[] payload = socketPool.readNextPacket();\n            bh.consume(payload);\n        }).get();\n    }\n}\n<\/code><\/pre>\n<p>The results were ugly. When pure non-blocking NIO channels were used, virtual threads matched platform threads in throughput while consuming a fraction of the memory footprint. But the moment we introduced a standard JDBC connection pool wrapped in blocking wrappers\u2014simulating a real enterprise database call\u2014the tail latencies diverged catastrophically.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Asynchronous_IO_Trap_and_Native_Thread_Pools\"><\/span>The Asynchronous I\/O Trap and Native Thread Pools<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Most enterprise applications do not use raw sockets. They use frameworks built on top of Netty or Jetty. These frameworks use their own event loops\u2014native thread pools pinned to CPU cores using epoll or kqueue. When you bridge virtual threads into a framework designed around async event loops, you introduce context-switching overhead that defeats the purpose of the abstraction.<\/p>\n<p>Consider the custom dispatcher implementation we had to write to prevent thread starvation in <code>com.fintech.ledger.io.SocketWorkerPool<\/code>:<\/p>\n<pre><code class=\"language-java\">\npackage com.fintech.ledger.io;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.AsynchronousSocketChannel;\nimport java.time.Duration;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\npublic class NativeEpollDispatcher {\n\n    private final AsynchronousSocketChannel channel;\n    private final Duration timeout;\n\n    public NativeEpollDispatcher(String host, int port, Duration timeout) throws IOException {\n        this.channel = AsynchronousSocketChannel.open();\n        this.timeout = timeout;\n        this.channel.connect(new InetSocketAddress(host, port)).get();\n    }\n\n    public byte[] readPayloadAsync() throws Exception {\n        ByteBuffer buffer = ByteBuffer.allocate(4096);\n        CompletableFuture&lt;Integer&gt; future = new CompletableFuture&lt;&gt;();\n\n        channel.read(buffer, null, new java.nio.channels.CompletionHandler&lt;Integer, Void&gt;() {\n            @Override\n            public void completed(Integer result, Void attachment) {\n                future.complete(result);\n            }\n\n            @Override\n            public void failed(Throwable exc, Void attachment) {\n                future.completeExceptionally(exc);\n            }\n        });\n\n        try {\n            future.get(timeout.toMillis(), TimeUnit.MILLISECONDS);\n            buffer.flip();\n            byte[] data = new byte[buffer.remaining()];\n            buffer.get(data);\n            return data;\n        } catch (TimeoutException e) {\n            channel.close();\n            throw new IOException(\"Socket read timed out after \" + timeout.toMillis() + \"ms\", e);\n        }\n    }\n}\n<\/code><\/pre>\n<p>If you call <code>future.get()<\/code> inside a virtual thread, it yields correctly. But if that completion handler executes inside a synchronized block or triggers a native library call, the carrier thread locks up. Under load tests running at 50,000 RPS, even a 0.1% pinning rate results in hundreds of carrier threads locking up per second. The JVM&#8217;s internal periodic unblocker thread has to step in, scanning pinned continuations and attempting recovery. That recovery mechanism introduces CPU spikes that look like garbage collection thrashing, but are actually thread scheduling stalls.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Garbage_Collection_Pressure_and_Continuation_Allocation\"><\/span>Garbage Collection Pressure and Continuation Allocation<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>People forget that virtual threads are still Java objects (`java.lang.VirtualThread`). Every time you execute `Thread.start()` or submit a task to a virtual thread executor, the JVM allocates a continuation object, a stack chunk, and associated scheduler references. Under high throughput, this creates massive churn in the Young Generation.<\/p>\n<p>We tracked allocation rates using GC logs with `-Xlog:gc*,safepoint=info`. When using platform thread pools with a fixed pool size of 200 threads, memory allocations were steady and predictable. Moving to unconstrained virtual threads without a custom bounding semaphore resulted in object creation rates exceeding 45 GB\/sec. The G1 garbage collector spent 12% of its CPU time in mixed GC cycles just cleaning up abandoned stack chunks and continuation frames.<\/p>\n<p>To fix this, we had to abandon unbounded task submission and implement a strict permit-based limiter:<\/p>\n<pre><code class=\"language-java\">\npackage com.fintech.ledger.io;\n\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.RejectedExecutionException;\n\npublic class BoundedVirtualThreadPool {\n\n    private final ExecutorService delegate;\n    private final Semaphore limiter;\n\n    public BoundedVirtualThreadPool(int maxConcurrentTasks) {\n        ThreadFactory factory = Thread.ofVirtual().name(\"fintech-worker-\", 0).factory();\n        this.delegate = Executors.newThreadPerTaskExecutor(factory);\n        this.limiter = new Semaphore(maxConcurrentTasks);\n    }\n\n    public void execute(Runnable task) {\n        if (!limiter.tryAcquire()) {\n            throw new RejectedExecutionException(\"Rate limit exceeded: carrier pools saturated.\");\n        }\n\n        delegate.submit(() -&gt; {\n            try {\n                task.run();\n            } finally {\n                limiter.release();\n            }\n        });\n    }\n}\n<\/code><\/pre>\n<h2><span class=\"ez-toc-section\" id=\"Flame_Graph_Realities_Where_the_Time_Goes\"><\/span>Flame Graph Realities: Where the Time Goes<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>When you pull up Async Profiler flame graphs of a misconfigured Java 21 payment gateway, the hot spots do not lie in business logic. They live in three specific places:<\/p>\n<ul>\n<li>`jdk.internal.vm.Continuation.yield` taking up 35% of CPU cycles due to excessive unmounting and remounting in tight polling loops.<\/li>\n<li>`java.util.concurrent.locks.ReentrantLock$Sync.lock` contention inside the ForkJoinPool worker queues when thousands of virtual threads wake up simultaneously upon receiving network packets.<\/li>\n<li>`sun.nio.ch.EPoll.epollWait` blocking native threads because asynchronous channel completion handlers are feeding back into synchronous thread pools.<\/li>\n<\/ul>\n<p>If you want virtual threads to perform in high-throughput network applications, you have to audit every single dependency in your dependency tree. Replace <code>synchronized<\/code> with <code>ReentrantLock<\/code> or java.util.concurrent atomic variables. Strip out blocking JDBC drivers and move to reactive or true non-linear drivers like r2dbc, or isolate your blocking legacy calls into a separate, isolated platform thread pool using <code>CompletableFuture.supplyAsync(task, legacyThreadPool)<\/code>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Production_Hardening_Checklist\"><\/span>Production Hardening Checklist<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>If you are deploying Java 21 virtual threads to production tomorrow, throw away the introductory <a href=\"https:\/\/itsupportwale.com\/blog\/\" title=\"Read more about blog\">blog<\/a> posts and verify these constraints on your staging cluster:<\/p>\n<ul>\n<li>Run your benchmark tests with `-XX:+UnlockDiagnosticVMOptions -XX:+ShowHiddenFrames` to identify exact continuation pinning points in your logs.<\/li>\n<li>Never pass virtual threads to legacy thread pools expecting performance gains; if you must interface with blocking code, use explicit bulkheads.<\/li>\n<li>Monitor `jdk.internal.vm.Continuation` allocation rates in JFR (Java Flight Recorder); if allocation rates outpace your survivor spaces, you are spawning too many short-lived virtual tasks without a rate limiter.<\/li>\n<li>Tune your JVM&#8217;s target parallelism using `-Djdk.virtualThreadScheduler.parallelism=` to match your physical core count, preventing hyper-threaded core thrashing under heavy I\/O loads.<\/li>\n<\/ul>\n<p>Virtual threads are a powerful addition to the JVM, but they are not a substitute for architectural discipline. Treat them with the same suspicion you would treat any other low-level concurrency primitive, profile your tail latencies under peak load, and check your flame graphs before pushing to production.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Post-Mortem: Socket Exhauster on Node 04 java.lang.OutOfMemoryError: Thread creation failed: Operating system resource exhausted at java.base\/java.lang.Thread.start(Thread.java:1539) at java.base\/java.lang.VirtualThread.start(VirtualThread.java:307) at com.fintech.ledger.io.SocketWorkerPool.dispatch(SocketWorkerPool.java:88) at com.fintech.ledger.GatewayServer.lambda$acceptConnections$3(GatewayServer.java:142) at java.base\/jdk.internal.vm.Continuation.enter(Continuation.java:340) &#8230; Caused by: java.io.UncheckedIOException: java.net.SocketException: Too many open files at com.fintech.ledger.io.NativeEpollDispatcher.register(NativeEpollDispatcher.java:211) It pinned. Hard. After three days of staring at raw kernel-bypass logs on Node 04, watching p99.9 latencies blow past &#8230; <a title=\"Benchmarking Java 21: Virtual Threads Latency Deep Dive\" class=\"read-more\" href=\"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/\" aria-label=\"Read more  on Benchmarking Java 21: Virtual Threads Latency Deep Dive\">Read more<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[699,697,695,698,696],"class_list":["post-5342","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-async-i-o","tag-benchmarking","tag-java-21","tag-latency","tag-virtual-threads"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Benchmarking Java 21: Virtual Threads Latency Deep Dive<\/title>\n<meta name=\"description\" content=\"In-depth benchmarking Java 21: Explore Virtual Threads latency with asynchronous I\/O &amp; native thread pools. Optimize your Java apps for speed.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Benchmarking Java 21: Virtual Threads Latency Deep Dive\" \/>\n<meta property=\"og:description\" content=\"In-depth benchmarking Java 21: Explore Virtual Threads latency with asynchronous I\/O &amp; native thread pools. Optimize your Java apps for speed.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/\" \/>\n<meta property=\"og:site_name\" content=\"ITSupportWale\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\" \/>\n<meta property=\"article:published_time\" content=\"2026-11-13T21:22:51+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"512\" \/>\n\t<meta property=\"og:image:height\" content=\"512\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Techie\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Techie\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/benchmarking-java-21-virtual-threads-latency-deep-dive\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/benchmarking-java-21-virtual-threads-latency-deep-dive\\\/\"},\"author\":{\"name\":\"Techie\",\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/#\\\/schema\\\/person\\\/8c5a2b3d36396e0a8fd91ec8242fd46d\"},\"headline\":\"Benchmarking Java 21: Virtual Threads Latency Deep Dive\",\"datePublished\":\"2026-11-13T21:22:51+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/benchmarking-java-21-virtual-threads-latency-deep-dive\\\/\"},\"wordCount\":1164,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/#organization\"},\"keywords\":[\"Async I\\\/O\",\"Benchmarking\",\"Java 21\",\"Latency\",\"Virtual Threads\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/benchmarking-java-21-virtual-threads-latency-deep-dive\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/benchmarking-java-21-virtual-threads-latency-deep-dive\\\/\",\"url\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/benchmarking-java-21-virtual-threads-latency-deep-dive\\\/\",\"name\":\"Benchmarking Java 21: Virtual Threads Latency Deep Dive\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-11-13T21:22:51+00:00\",\"description\":\"In-depth benchmarking Java 21: Explore Virtual Threads latency with asynchronous I\\\/O & native thread pools. Optimize your Java apps for speed.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/benchmarking-java-21-virtual-threads-latency-deep-dive\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/benchmarking-java-21-virtual-threads-latency-deep-dive\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/benchmarking-java-21-virtual-threads-latency-deep-dive\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Benchmarking Java 21: Virtual Threads Latency Deep Dive\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/\",\"name\":\"ITSupportWale\",\"description\":\"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides\",\"publisher\":{\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/#organization\",\"name\":\"itsupportwale\",\"url\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/09\\\/cropped-Logo-trans-without-slogan.png\",\"contentUrl\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/09\\\/cropped-Logo-trans-without-slogan.png\",\"width\":1119,\"height\":144,\"caption\":\"itsupportwale\"},\"image\":{\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/Itsupportwale-298547177495978\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/#\\\/schema\\\/person\\\/8c5a2b3d36396e0a8fd91ec8242fd46d\",\"name\":\"Techie\",\"sameAs\":[\"https:\\\/\\\/itsupportwale.com\",\"iswblogadmin\"],\"url\":\"https:\\\/\\\/itsupportwale.com\\\/blog\\\/author\\\/iswblogadmin\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Benchmarking Java 21: Virtual Threads Latency Deep Dive","description":"In-depth benchmarking Java 21: Explore Virtual Threads latency with asynchronous I\/O & native thread pools. Optimize your Java apps for speed.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/","og_locale":"en_US","og_type":"article","og_title":"Benchmarking Java 21: Virtual Threads Latency Deep Dive","og_description":"In-depth benchmarking Java 21: Explore Virtual Threads latency with asynchronous I\/O & native thread pools. Optimize your Java apps for speed.","og_url":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/","og_site_name":"ITSupportWale","article_publisher":"https:\/\/www.facebook.com\/Itsupportwale-298547177495978","article_published_time":"2026-11-13T21:22:51+00:00","og_image":[{"width":512,"height":512,"url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png","type":"image\/png"}],"author":"Techie","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Techie","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#article","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/"},"author":{"name":"Techie","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d"},"headline":"Benchmarking Java 21: Virtual Threads Latency Deep Dive","datePublished":"2026-11-13T21:22:51+00:00","mainEntityOfPage":{"@id":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/"},"wordCount":1164,"commentCount":0,"publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"keywords":["Async I\/O","Benchmarking","Java 21","Latency","Virtual Threads"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/","url":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/","name":"Benchmarking Java 21: Virtual Threads Latency Deep Dive","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/#website"},"datePublished":"2026-11-13T21:22:51+00:00","description":"In-depth benchmarking Java 21: Explore Virtual Threads latency with asynchronous I\/O & native thread pools. Optimize your Java apps for speed.","breadcrumb":{"@id":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/itsupportwale.com\/blog\/benchmarking-java-21-virtual-threads-latency-deep-dive\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/itsupportwale.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Benchmarking Java 21: Virtual Threads Latency Deep Dive"}]},{"@type":"WebSite","@id":"https:\/\/itsupportwale.com\/blog\/#website","url":"https:\/\/itsupportwale.com\/blog\/","name":"ITSupportWale","description":"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides","publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/itsupportwale.com\/blog\/#organization","name":"itsupportwale","url":"https:\/\/itsupportwale.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","contentUrl":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","width":1119,"height":144,"caption":"itsupportwale"},"image":{"@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Itsupportwale-298547177495978"]},{"@type":"Person","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d","name":"Techie","sameAs":["https:\/\/itsupportwale.com","iswblogadmin"],"url":"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/"}]}},"_links":{"self":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/5342","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/comments?post=5342"}],"version-history":[{"count":0,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/5342\/revisions"}],"wp:attachment":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/media?parent=5342"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/categories?post=5342"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/tags?post=5342"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}