Java 27 and 26: runnable demos and captured output for every JEP, plus version lanes

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01B38FGKKam5SCGgwgduVAh3
This commit is contained in:
2026-09-21 15:08:50 +00:00
committed by Claude
co-authored by Claude Sonnet 5
commit f59c1de96d
152 changed files with 5049 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
build/
*.class
*.jfr
.lib/
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Ankur Mhatre
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+75
View File
@@ -0,0 +1,75 @@
# javademos
Runnable companion code for the ankurm.com post **"Java 27 Is Out: Every JEP, Plus the Java 26 Changes You Skipped"**, and for the Java version guides
(<https://ankurm.com/java-21-to-25-lts-features/>). Every number and transcript in the post comes from a file in `docs/output/`, produced by a script in `scripts/`.
## What was tested against
| Thing | Version | Note |
|---|---|---|
| JDK 21 (LTS) | Amazon Corretto 21.0.12.1 | the start of the 21 to 25 lane |
| JDK 25 (LTS) | Temurin 25.0.4.1+1 | the baseline for "broke on 26" |
| JDK 26 | Amazon Corretto 26.0.2.1 | GA 2026-03-17, non-LTS |
| JDK 27 | Amazon Corretto 27+35 | GA 2026-09-15, non-LTS |
| Containers | `amazoncorretto:23`, `:24` (when did it change?), `:26`, `:27` | same vendor for both, so only the JDK version changes |
| JOL | 0.17 | downloaded and sha1-checked by `scripts/object-headers.sh` |
Next LTS: Java 29, September 2027 (Oracle Java SE Support Roadmap). Layout: `g1-container/`, `object-headers/`, `jep-tour/` (27 features and `broken/` 26-era sources),
`recap26/` (the Java 26 lane), `lanes/` (the 21 to 25 hub demos, the AI prompts in `lanes/prompts/` and the build-file demo in `lanes/upgrade/`), `other-changes/`, `api-diff/`.
## Quickstart
```bash
export JDK21=/path/to/jdk-21 JDK25=/path/to/jdk-25 JDK26=/path/to/jdk-26 JDK27=/path/to/jdk-27 # defaults are in scripts/env.sh
./scripts/jep-tour.sh # the five 27 feature demos, self-asserting
./scripts/recap26.sh # the 26 recap lane
./scripts/lanes.sh # the 21 to 25 hub claims, checked on 21, 23, 24, 25, 26, 27
./scripts/upgrade.sh # the build-file half: Maven, Gradle, Lombok, --release, jdeps (needs Maven Central)
./scripts/run-all.sh # regenerate every docs/output/*.txt (about 15-20 minutes; needs Docker)
```
Timings, RSS, GC counts and the Vector species are machine dependent; treat them as shape, not results. Everything else is byte-stable.
## Chapters
| # | Chapter |
|---|---|
| 1 | [What shipped, and what to believe](docs/01-what-shipped.md) |
| 2 | [G1 becomes the default, even on one CPU (JEP 523)](docs/02-g1-default.md) |
| 3 | [Compact object headers (JEP 534)](docs/03-compact-headers.md) |
| 4 | [Post-quantum TLS (JEP 527)](docs/04-post-quantum-tls.md) |
| 5 | [Primitive patterns (JEP 532)](docs/05-primitive-patterns.md) |
| 6 | [Lazy constants (JEP 531)](docs/06-lazy-constants.md) |
| 7 | [Structured concurrency (JEP 533)](docs/07-structured-concurrency.md) |
| 8 | [PEM encodings (JEP 538)](docs/08-pem-api.md) |
| 9 | [JFR redaction (JEP 536) and the Vector API (JEP 537)](docs/09-jfr-redaction-and-vector.md) |
| 10 | [Other 27 changes](docs/10-other-27-changes.md) |
| 11 | [Recap: the Java 26 changes you skipped](docs/11-recap-26.md) |
| 12 | [Version lanes: 25 to 29](docs/12-lanes-25-to-29.md) |
| 13 | [Upgrade checklist](docs/13-upgrade-checklist.md) |
| 14 | [The 21 to 25 lane: every claim in the hub posts, checked](docs/14-lanes-21-to-25.md) |
| 15 | [Build files: Maven, Gradle and Lombok on the way to 25](docs/15-build-files.md) |
## Captured output (`docs/output/`)
| File | Produced by | What it is |
|---|---|---|
| `01`-`03` | `g1-default.sh` | which collector each JDK picks, by container size; the opt-out |
| `04`-`06` | `g1-cost.sh`, `g1-tuning.sh` | what G1 costs on one CPU, and two tested explanations |
| `10`-`15` | `object-headers.sh` | JOL instance sizes on 26, 27, 27 opted out; the JOL record failure and workaround |
| `20-broken-*` | `broken-on-27.sh` | javac's own messages for 26-era preview code on 27 |
| `21`-`25` | `jep-tour.sh` | the five 27 feature demos |
| `30`-`32` | `tls-pq.sh` | key-exchange matrix, ClientHello size, the opt-out |
| `40` | `jfr-redaction.sh` | five redaction configurations |
| `50`-`52` | `api-diff.sh` | public API diffs, 26 to 27, 25 to 26, Vector only |
| `60`-`62` | `other-changes.sh` | removed options, behaviour changes, new 27 API |
| `70`-`75` | `recap26.sh` | final-field mutation, AOT cache matrix and startup, removals, 26 API, HTTP/3 |
| `92`-`97` | `upgrade.sh` | Maven release 25 on JDK 21, 25, 27, Gradle 8 vs 9 on JDK 25, `--release` vs `-source/-target`, Lombok and Mockito by version, `jdeps --jdk-internals` |
| `80`-`91` | `lanes.sh` | the 21 to 25 hub claims on JDK 21, 23, 24, 25, 26, 27: scoped values, structured concurrency, finalization, pinning, ZGC flags, warnings |
## Not reproduced
`jdk.java.net/27` and `openjdk.org/jeps` returned 403 while this was written; the JEP list was cross-checked against four secondary sources and the JDK binaries themselves.
HTTP/3 was not spoken to a real HTTP/3 server (no UDP egress). JEP 522 was not benchmarked. JDK-8377013 could not be reproduced.
MIT licensed, see `LICENSE`.
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
"""Diff two javap dumps produced by dump_api.py and report changes to PUBLIC API only.
usage: apidiff.py <old-dump> <new-dump> [--vector-only]
A class is reported only if its declaration starts with 'public' in the old or the new dump, so
package-private implementation classes (which javap -public still lists) do not drown the signal.
"""
import collections
import re
import sys
def parse(path):
classes = collections.OrderedDict()
decl = {}
cur = None
for line in open(path):
line = line.rstrip("\n")
if line.startswith("#####") or line.startswith("Compiled from") or not line.strip():
continue
if not line.startswith(" "):
m = re.search(r"(?:class|interface|enum|@interface|record)\s+([\w.$]+)", line)
cur = m.group(1) if m else line
classes[cur] = set()
decl[cur] = line
elif cur is not None and line.strip() != "}":
classes[cur].add(line.strip())
return classes, decl
def is_public(decl_line):
return decl_line.startswith("public ") or decl_line.startswith("protected ")
def main():
old, old_decl = parse(sys.argv[1])
new, new_decl = parse(sys.argv[2])
vector_only = "--vector-only" in sys.argv
def keep(name):
if vector_only and not name.startswith("jdk.incubator.vector."):
return False
if name.startswith("file") or "Error: Access Flags" in old_decl.get(name, "") + new_decl.get(name, ""):
return False
return is_public(old_decl.get(name, "")) or is_public(new_decl.get(name, ""))
print("## public classes removed")
for c in sorted(set(old) - set(new)):
if keep(c):
print(" " + c)
print("## public classes added")
for c in sorted(set(new) - set(old)):
if keep(c):
print(" " + c)
print("## public class changes")
for c in sorted(set(old) & set(new)):
if not keep(c):
continue
removed = old[c] - new[c]
added = new[c] - old[c]
d_old, d_new = old_decl[c], new_decl[c]
if removed or added or d_old != d_new:
print(c)
if d_old != d_new:
print(" - " + d_old.strip())
print(" + " + d_new.strip())
for x in sorted(removed):
print(" - " + x)
for x in sorted(added):
print(" + " + x)
main()
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env python3
"""Dump the public API signatures of every exported class in the JDK's java.se-ish modules.
usage: dump_api.py <JAVA_HOME> <out-file>"""
import subprocess, sys, re, os
home, out = sys.argv[1], sys.argv[2]
env = {k:v for k,v in os.environ.items() if k!='JAVA_TOOL_OPTIONS'}
jimage = subprocess.run([f'{home}/bin/jimage','list',f'{home}/lib/modules'],capture_output=True,text=True,env=env).stdout
mods = {}
cur=None
for line in jimage.splitlines():
if line.startswith('Module: '): cur=line.split(': ')[1].strip(); mods[cur]=[]
elif cur and line.strip().endswith('.class'):
mods[cur].append(line.strip()[:-6])
WANT = re.compile(r'^(java|jdk\.(jfr|httpserver|incubator\.vector|management|net|jshell|jartool|jlink|jpackage|javadoc|compiler|jcmd|unsupported|security\.auth)|javafx)')
res=[]
for m,classes in sorted(mods.items()):
if not WANT.match(m): continue
names=[]
for c in classes:
if c=='module-info': continue
pk=c.rsplit('/',1)[0].replace('/','.') if '/' in c else ''
if re.search(r'(^|\.)(internal|impl)(\.|$)|^sun\.|^com\.sun\.(?!net\.httpserver|management|security\.auth|source)',pk): continue
names.append(c.replace('/','.'))
if not names: continue
# javap handles binary names with $ for nested
names=[n for n in names if not re.search(r'\$\d',n)]
txt=''
for i in range(0,len(names),200):
p=subprocess.run([f'{home}/bin/javap','--module',m,'-public']+names[i:i+200],capture_output=True,text=True,env=env)
txt+=p.stdout
res.append(f'##### MODULE {m}\n'+txt)
open(out,'w').write('\n'.join(res))
print(out, sum(len(r) for r in res)//1024,'KB')
+52
View File
@@ -0,0 +1,52 @@
# 1. What shipped, and what to believe
Next: [2. G1 becomes the default](02-g1-default.md)
Java 27 reached General Availability on 2026-09-15. It is **not** an LTS: Oracle's support roadmap lists Premier support for 27 until March 2027,
and names Java 29 (September 2027) as the next LTS after 25. Everything in this repository was run on real JDK binaries; where a claim comes from
prose rather than from a run, the chapter says so.
## The nine JEPs in 27
| JEP | Title | Status in 27 | Chapter |
|---|---|---|---|
| 523 | Make G1 the Default Garbage Collector in All Environments | final | [2](02-g1-default.md) |
| 534 | Compact Object Headers by Default | final | [3](03-compact-headers.md) |
| 527 | Post-Quantum Hybrid Key Exchange for TLS 1.3 | final | [4](04-post-quantum-tls.md) |
| 532 | Primitive Types in Patterns, instanceof, and switch | fifth preview | [5](05-primitive-patterns.md) |
| 531 | Lazy Constants | third preview | [6](06-lazy-constants.md) |
| 533 | Structured Concurrency | seventh preview | [7](07-structured-concurrency.md) |
| 538 | PEM Encodings of Cryptographic Objects | third preview | [8](08-pem-api.md) |
| 536 | JFR: In-Process Data Redaction | final | [9](09-jfr-redaction-and-vector.md) |
| 537 | Vector API | twelfth incubator | [9](09-jfr-redaction-and-vector.md) |
## The ten JEPs in 26 (the release many teams skipped)
| JEP | Title | Status in 26 | Chapter |
|---|---|---|---|
| 500 | Prepare to Make Final Mean Final | final | [11](11-recap-26.md) |
| 504 | Remove the Applet API | final | [11](11-recap-26.md) |
| 516 | Ahead-of-Time Object Caching with Any GC | final | [11](11-recap-26.md) |
| 517 | HTTP/3 for the HTTP Client API | final | [11](11-recap-26.md) |
| 522 | G1 GC: Improve Throughput by Reducing Synchronization | final | [11](11-recap-26.md) (not measured here) |
| 524 | PEM Encodings of Cryptographic Objects | second preview | [8](08-pem-api.md) |
| 525 | Structured Concurrency | sixth preview | [7](07-structured-concurrency.md) |
| 526 | Lazy Constants | second preview | [6](06-lazy-constants.md) |
| 529 | Vector API | eleventh incubator | [9](09-jfr-redaction-and-vector.md) |
| 530 | Primitive Types in Patterns, instanceof, and switch | fourth preview | [5](05-primitive-patterns.md) |
## How the list was verified
openjdk.org/jeps and jdk.java.net/27 returned HTTP 403 from the sandbox used to write this, and no workaround was attempted. The lists were
cross-checked between the Inside.java release announcement, Oracle's release blog, InfoWorld's and foojay's write-ups, and then against the
**binaries**: the JDK 27 and 26 builds used here were diffed class by class ([51](output/51-api-diff-26-to-27.txt),
[52](output/52-api-diff-25-to-26.txt)), which is how two API names in a secondary write-up were corrected. Treat the JEP titles above as
"as reported by four secondary sources"; treat every behaviour as "observed on Corretto 26.0.2.1 / 27+35".
## What was not reproduced
* JDK-8377013 (a reported `TimeZone.getDefault()` regression) could not be reproduced and is left out.
* HTTP/3 was never spoken to a real HTTP/3 server: the sandbox has no UDP egress ([11](11-recap-26.md)).
* JEP 522 (G1 synchronization) was not benchmarked.
Next: [2. G1 becomes the default](02-g1-default.md)
+56
View File
@@ -0,0 +1,56 @@
# 2. G1 becomes the default, even on one CPU (JEP 523)
Prev: [1. What shipped](01-what-shipped.md) &middot; Next: [3. Compact object headers](03-compact-headers.md)
## The old rule
Since JDK 9 the JVM has chosen its collector by "server-class machine" ergonomics: G1 if it sees at least **two** CPUs and roughly **1792 MB**
of memory, Serial otherwise. A container limited to one CPU, or to 1 GB, quietly got Serial. JEP 523 removes the exception: G1 is chosen
in every environment.
## What the containers say
[`scripts/g1-default.sh`](../scripts/g1-default.sh) runs [`GcReport`](../g1-container/src/GcReport.java) in `amazoncorretto:26` and `:27`
containers. Only the JDK version and the limits change.
| Container | JDK 26 picks | JDK 27 picks | Transcript |
|---|---|---|---|
| `--cpus=1 --memory=2g` | Serial (`UseSerialGC ... ERGONOMIC`) | G1 | [01](output/01-g1-default-1cpu-vs-2cpu.txt) |
| `--cpus=2 --memory=2g` | G1 | G1 | [01](output/01-g1-default-1cpu-vs-2cpu.txt) |
| `--cpus=2 --memory=1g` | Serial | G1 | [02](output/02-g1-default-memory.txt) |
So both halves of the old rule are gone: CPU count *and* memory limit. An explicit `-XX:+UseSerialGC` still wins
([03](output/03-g1-default-optout.txt)); its origin shows as `VM_CREATION`, not `ERGONOMIC`.
## What it costs when your container is small
[`Workload`](../g1-container/src/Workload.java) allocates 50,000 short-lived objects per batch, keeps about 1% of them, and reports wall time, GC
count and time, the slowest single batch and peak RSS. Three runs per configuration, 1 CPU, 2 GB ([04](output/04-g1-cost-1cpu.txt)):
| 4000 batches | wall time | GC count / total GC time | slowest batch | peak RSS |
|---|---|---|---|---|
| 26, default (Serial) | 6.0-6.4 s | 1275 / ~1.85 s | 115-142 ms | 557 MB |
| 27, default (G1) | 8.2-8.4 s | 140 / ~1.98 s | 35-41 ms | 547 MB |
| 27, `-XX:+UseSerialGC` | 5.0-5.7 s | 1264 / ~1.66 s | 119-128 ms | 557 MB |
The shape, not the digits: on one CPU, G1 has roughly **3x shorter worst-case stalls** and takes **about a third longer** to finish the same allocation-heavy work.
G1's concurrent work has to share the only CPU with your application.
A short-lived process makes it look worse ([05](output/05-g1-cost-1cpu-short.txt), 400 batches): 26 finishes in ~0.53 s using ~102 MB RSS; 27 with G1 takes
~1.0 s and touches ~492 MB, because G1 grows the heap toward the 512 MB default maximum instead of collecting early. Serial on 27 matches 26 (~0.52 s, ~98 MB).
## Two suspects, tested
[`scripts/g1-tuning.sh`](../scripts/g1-tuning.sh) ([06](output/06-g1-tuning-1cpu.txt)):
1. JDK 27 also changed the heap free ratios G1 uses (`MinHeapFreeRatio` 40 -> 0, `MaxHeapFreeRatio` 70 -> 100). Restoring 40/70 changed nothing measurable.
2. G1 being allowed to grow toward `MaxHeapSize`. Capping the heap at `-Xmx256m` cut peak RSS from ~492 MB to ~294 MB with about the same wall time.
## Advice
* Nothing to do if you already set a collector explicitly.
* If you were relying on the implicit Serial in small containers and you are throughput- or footprint-bound, pin it: `-XX:+UseSerialGC`.
* If you are happy with G1, **set `-Xmx`** (or `-XX:MaxRAMPercentage`) so the heap does not sprawl to the default maximum.
* Note `-XX:InitiatingHeapOccupancyPercent` is deprecated in 27 in favour of `-XX:G1IHOP` ([60](output/60-removed-options.txt)).
Prev: [1. What shipped](01-what-shipped.md) &middot; Next: [3. Compact object headers](03-compact-headers.md)
+36
View File
@@ -0,0 +1,36 @@
# 3. Compact object headers are the default (JEP 534)
Prev: [2. G1](02-g1-default.md) &middot; Next: [4. Post-quantum TLS](04-post-quantum-tls.md)
Every Java object starts with a header. On JDK 26 with default settings it is **12 bytes** (8 bytes mark word plus a 4-byte compressed class pointer).
With compact object headers the two are squeezed into **8 bytes**. JEP 534 makes that the default in 27; `-XX:-UseCompactObjectHeaders` turns it off.
## Measured with JOL
[`scripts/object-headers.sh`](../scripts/object-headers.sh) downloads JOL 0.17 (sha1-checked) and runs [`HeaderDemo`](../object-headers/src/HeaderDemo.java) three ways.
The summary is [15](output/15-headers-summary.txt); raw runs are [10](output/10-headers-jdk26.txt) (26), [11](output/11-headers-jdk27.txt) (27),
[12](output/12-headers-jdk27-opt-out.txt) (27 with the opt-out, which reproduces 26 exactly).
| Object | JDK 26 | JDK 27 |
|---|---|---|
| `Object` | 16 B | 8 B |
| `Point { int x; int y; }` | 24 B | 16 B |
| `Order { long id; int quantity; boolean paid; String customer; }` | 32 B | 32 B |
| boxed `Long` | 24 B | 16 B |
| boxed `Integer` | 16 B | 16 B |
| `int[3]` | 32 B | 24 B |
| 1,000,000 boxed `Long` in an `ArrayList` | 28.9 MB | 20.9 MB |
| 1,000,000 `Point` in an `ArrayList` | 28.9 MB | 20.9 MB |
| 1,000,000 boxed `Integer` in an `ArrayList` | 20.9 MB | 20.9 MB |
The pattern to notice: saving 4 bytes only helps when it crosses an 8-byte alignment boundary. `Order` and boxed `Integer` do not move, because padding
was already absorbing the difference. Your heap will not shrink by "4 bytes times object count"; it shrinks by the number of objects that were sitting just past a boundary.
## Two things that bite
* **JOL 0.17 cannot inspect a `record`.** `Unsafe.objectFieldOffset` refuses records ([13](output/13-jol-record-failure.txt)). The workaround
`-Djol.magicFieldOffset=true` prints a layout but shows the header mark as `N/A` ([14](output/14-jol-record-workaround.txt)).
* **Anything that hard-codes 12 bytes** (off-heap size estimators, "object overhead" constants in capacity plans, tests that assert `Instance size`) is now wrong by default.
`-XX:+UseCompressedClassPointers` is also no longer an option in 27: the JVM prints "Ignoring option UseCompressedClassPointers; support was removed in 27.0" ([60](output/60-removed-options.txt)).
Prev: [2. G1](02-g1-default.md) &middot; Next: [4. Post-quantum TLS](04-post-quantum-tls.md)
+36
View File
@@ -0,0 +1,36 @@
# 4. Post-quantum hybrid key exchange in TLS 1.3 (JEP 527)
Prev: [3. Compact headers](03-compact-headers.md) &middot; Next: [5. Primitive patterns](05-primitive-patterns.md)
JDK 27's default TLS 1.3 client offers the hybrid group **X25519MLKEM768** first: an ordinary X25519 exchange and an ML-KEM-768 key encapsulation combined, so a
recorded session stays safe even if a large quantum computer later breaks X25519.
## Who negotiates what
[`scripts/tls-pq.sh`](../scripts/tls-pq.sh) starts a loopback TLS server with [`TlsPeer`](../jep-tour/src/TlsPeer.java) on one JDK and connects with a client on
the other, for all four pairings, with `-Djavax.net.debug=ssl:handshake` ([30](output/30-tls-groups-matrix.txt)).
| Client | Server | Group selected |
|---|---|---|
| 26 | 26 | x25519 |
| 27 | 27 | **X25519MLKEM768** |
| 27 | 26 | x25519 (the 26 server does not know the hybrid group) |
| 26 | 27 | x25519 (the 26 client never offered it) |
Nothing fails when the peers disagree: the hybrid only happens when both ends are on 27 (or another stack that supports it).
## The catch: a bigger first packet
[`ClientHelloSize`](../jep-tour/src/ClientHelloSize.java) captures the first TLS record ([31](output/31-tls-clienthello-size.txt)):
| Client | ClientHello record | Fits one TCP segment (1460-byte MSS)? |
|---|---|---|
| JDK 26 | 417 bytes | yes |
| JDK 27 | 1573 bytes | **no** |
| JDK 27, `-Djdk.tls.namedGroups=x25519,secp256r1` | 408 bytes | yes |
The ML-KEM public key alone is 1184 bytes, so the ClientHello no longer fits in one segment. Middleboxes that mishandle a ClientHello spanning segments are the
known failure mode of large post-quantum ClientHellos; **that failure was not observed here** (loopback has no middleboxes). If you meet it, the opt-out is the system
property above, and the client then behaves like 26 ([32](output/32-tls-opt-out.txt)).
Prev: [3. Compact headers](03-compact-headers.md) &middot; Next: [5. Primitive patterns](05-primitive-patterns.md)
+32
View File
@@ -0,0 +1,32 @@
# 5. Primitive types in patterns (JEP 532, fifth preview)
Prev: [4. Post-quantum TLS](04-post-quantum-tls.md) &middot; Next: [6. Lazy constants](06-lazy-constants.md)
Compile and run with `--enable-preview --release 27`. Source: [`PrimitivePatterns`](../jep-tour/src/PrimitivePatterns.java), transcript [23](output/23-primitive-patterns.txt).
## The idea in one sentence
A pattern may now name a primitive type, and the test is **"can this value be converted to that type without losing information?"**. It is an exactness test, not a cast.
```java
int small = 100, big = 300;
small instanceof byte // true : 100 fits
big instanceof byte // false : a byte holds -128..127
16_777_216 instanceof float // true
16_777_217 instanceof float // false : a float has a 24-bit significand
```
You can also `switch` on a `long`, `float`, `double` or `boolean`, use `case int` against an `Integer`, and put a narrowing primitive in a record pattern:
`Reading(byte c)` matches `Reading[celsius=36]` but not `Reading[celsius=4000]` ([23](output/23-primitive-patterns.txt)).
## The compile error you will meet
Dominance rules apply. Put `case Integer i` before `case int primitive` and the second label can never match:
```
error: this case label is dominated by a preceding case label
```
The real text is in [20-broken-dominated.txt](output/20-broken-dominated.txt); source in [`Dominated.java`](../jep-tour/src/broken/Dominated.java).
Prev: [4. Post-quantum TLS](04-post-quantum-tls.md) &middot; Next: [6. Lazy constants](06-lazy-constants.md)
+23
View File
@@ -0,0 +1,23 @@
# 6. Lazy constants (JEP 531, third preview)
Prev: [5. Primitive patterns](05-primitive-patterns.md) &middot; Next: [7. Structured concurrency](07-structured-concurrency.md)
Compile and run with `--enable-preview --release 27`. Source: [`LazyDemo`](../jep-tour/src/LazyDemo.java), transcript [22](output/22-lazy.txt).
A **lazy constant** holds a value that is computed the first time somebody asks for it, exactly once, even when many threads ask at the same instant,
after which the JVM may treat it like a `final` field. It replaces the hand-written holder-class idiom and the double-checked-locking field.
```java
static final LazyConstant<Settings> SETTINGS = LazyConstant.of(LazyDemo::loadSettings);
Settings s = SETTINGS.get(); // computes on first call, returns the same instance afterwards
```
What the transcript shows, each backed by an assertion: the supplier ran once; 64 virtual threads racing on `get()` caused one initialisation; `List.ofLazy(5, i -> ...)`
computed only element 3 after `get(3)`; `Map.ofLazy(keys, f)` computed only the requested key; `Set.ofLazy(Set.of(2..7), isPrime)` answers `contains` on demand.
## What changed since 26 (the compile errors)
* `LazyConstant.orElse(...)` and `isInitialized()` are **gone** in 27 ([20-broken-lazy26.txt](output/20-broken-lazy26.txt); the class is `LazyConstant` in both, the old `StableValue` was already removed in 26 - [52](output/52-api-diff-25-to-26.txt)).
* `Set.ofLazy(Set, Predicate)` is new in 27 ([62](output/62-new-api.txt), [51](output/51-api-diff-26-to-27.txt)).
Prev: [5. Primitive patterns](05-primitive-patterns.md) &middot; Next: [7. Structured concurrency](07-structured-concurrency.md)
+27
View File
@@ -0,0 +1,27 @@
# 7. Structured concurrency (JEP 533, seventh preview)
Prev: [6. Lazy constants](06-lazy-constants.md) &middot; Next: [8. PEM](08-pem-api.md)
Compile and run with `--enable-preview --release 27`. Source: [`StructuredDemo`](../jep-tour/src/StructuredDemo.java), transcript [21](output/21-structured.txt).
The 27 API, as exercised:
```java
try (var scope = StructuredTaskScope.open(Joiner.<String>allSuccessfulOrThrow())) {
scope.fork(StructuredDemo::fetchPrice);
scope.fork(StructuredDemo::fetchStock);
List<String> results = scope.join(); // [price=42, stock=7], in fork order
}
```
* A failing subtask makes `join()` throw `ExecutionException` with the original as its cause.
* **New in 27:** `StructuredTaskScope` and `Joiner` gained a third type parameter, the exception `join()` throws. `Joiner.<String, OrderFailed>allSuccessfulOrThrow(OrderFailed::new)` makes `join()` throw *your* type.
* A timeout is a scope option (`cfg -> cfg.withTimeout(Duration.ofMillis(100))`) and surfaces as `CancelledByTimeoutException`.
## What broke from 26
[`StructuredScope26.java`](../jep-tour/src/broken/StructuredScope26.java) compiles on 26 and fails on 27 with three errors ([20-broken-structuredscope26.txt](output/20-broken-structuredscope26.txt)):
`wrong number of type arguments; required 3`, and `FailedException` / `TimeoutException` no longer exist as nested classes
([51](output/51-api-diff-26-to-27.txt): removed `StructuredTaskScope$FailedException`, `$TimeoutException`; added `$CancelledByTimeoutException`).
Prev: [6. Lazy constants](06-lazy-constants.md) &middot; Next: [8. PEM](08-pem-api.md)
+27
View File
@@ -0,0 +1,27 @@
# 8. PEM encodings (JEP 538, third preview)
Prev: [7. Structured concurrency](07-structured-concurrency.md) &middot; Next: [9. JFR redaction and the Vector API](09-jfr-redaction-and-vector.md)
Compile and run with `--enable-preview --release 27`. Source: [`PemDemo`](../jep-tour/src/PemDemo.java), transcript [24](output/24-pem.txt).
PEM is the `-----BEGIN ...-----` text format that every key and certificate file on a Linux box uses. The preview API replaces hand-rolled Base64 and header strings:
```java
String pem = PEMEncoder.of().encodeToString(keyPair.getPublic()); // -----BEGIN PUBLIC KEY-----
PublicKey back = PEMDecoder.of().decode(pem, PublicKey.class);
String enc = PEMEncoder.of().withEncryption(password).encodeToString(privateKey); // ENCRYPTED PRIVATE KEY
```
Each round trip is asserted in the demo, including: without the password `decode` returns a still-encrypted `EncryptedPrivateKeyInfo`; with `withDecryption(password)` you get the original key back.
## The trap: `new PEM(type, byte[])` does not Base64-encode
The bytes you pass are treated as the **already Base64-encoded** content and written between the header and footer verbatim
([24](output/24-pem.txt): the demo prints `hello, pem` in clear between `BEGIN ANKURM DEMO` and `END ANKURM DEMO`). To get a valid PEM, Base64-encode first and pass that; then `content()` is the Base64 text and `decode()` gives your payload back. This cost a failed assertion while writing this repository.
## What moved between 26 and 27
[`Pem26Style.java`](../jep-tour/src/broken/Pem26Style.java) compiles on 26 and produces three errors on 27 ([20-broken-pem26style.txt](output/20-broken-pem26style.txt)): `DEREncodable` became `BinaryEncodable`;
`PEMDecoder.withFactory(Provider)` is gone; `PEM.content()` now returns `byte[]` rather than `String`.
Prev: [7. Structured concurrency](07-structured-concurrency.md) &middot; Next: [9. JFR redaction and the Vector API](09-jfr-redaction-and-vector.md)
+28
View File
@@ -0,0 +1,28 @@
# 9. JFR redaction (JEP 536) and the Vector API (JEP 537)
Prev: [8. PEM](08-pem-api.md) &middot; Next: [10. Other 27 changes](10-other-27-changes.md)
## JFR redacts secrets in the recording by default
A JFR file records the JVM's command-line arguments, system properties and environment variables. Those routinely hold passwords and tokens, and recordings get attached to tickets.
[`scripts/jfr-redaction.sh`](../scripts/jfr-redaction.sh) starts an [`Idle`](../jep-tour/src/Idle.java) process with `-Dapi.token=abc123 -Dregion=ap-south-1 ... --password=hunter2 --user=ankur`
and `DB_PASSWORD=hunter2` in the environment, then prints what reached the file ([40](output/40-jfr-redaction.txt)):
| Configuration | `-Dapi.token` | `--password` | `DB_PASSWORD` | `region` |
|---|---|---|---|---|
| JDK 26 | abc123 | hunter2 | hunter2 | ap-south-1 |
| JDK 27 default | `[REDACTED]` | `[REDACTED]` | `[REDACTED]` | ap-south-1 |
| 27, `redact-argument=--user*,redact-key=region` | abc123 (exposed) | hunter2 (exposed) | hunter2 (exposed) | `[REDACTED]` |
| 27, `redact-argument=+--user*,redact-key=+region` | `[REDACTED]` | `[REDACTED]` | `[REDACTED]` | `[REDACTED]` |
| 27, `redact-argument=none,redact-key=none` | abc123 | hunter2 | hunter2 | ap-south-1 |
**The trap in row 3:** a list you supply *replaces* the built-in patterns. Without the `+` prefix you have silently turned the defaults off for everything you did not list.
## The Vector API is still incubating
[`VectorDemo`](../jep-tour/src/VectorDemo.java) ([25](output/25-vector.txt)) computes a dot product with and without `jdk.incubator.vector` and asserts they agree within 0.1%.
The same class file runs on 26 (eleventh incubator, JEP 529) and 27 (twelfth, JEP 537): the only public API change between them is that `VectorOperators` changed from `abstract` to `final`
([50](output/50-vector-api-surface.txt)). The preferred species is machine dependent (`S_512_BIT`, 16 float lanes, on the machine that wrote the transcript).
For the API itself see the ankurm.com guide: <https://ankurm.com/java-vector-api-jep-537-simd-guide/>.
Prev: [8. PEM](08-pem-api.md) &middot; Next: [10. Other 27 changes](10-other-27-changes.md)
+25
View File
@@ -0,0 +1,25 @@
# 10. The changes that are not JEPs but will still break a Monday
Prev: [9. JFR and Vector](09-jfr-redaction-and-vector.md) &middot; Next: [11. Recap of Java 26](11-recap-26.md)
Script: [`scripts/other-changes.sh`](../scripts/other-changes.sh). All from real launches; each option runs `java <option> -version` on 26 and on 27 ([60](output/60-removed-options.txt)).
| Option / property | JDK 26 | JDK 27 |
|---|---|---|
| `-noverify`, `-Xverify:none` | deprecation warning | **`Unrecognized option`, the JVM does not start** |
| `-noclassgc` | deprecation warning | **`Unrecognized option`, the JVM does not start** |
| `-XX:+UseCompressedClassPointers` | deprecated in 25 | ignored: "support was removed in 27.0" |
| `-XX:InitiatingHeapOccupancyPercent=40` | accepted | deprecated; use `-XX:G1IHOP` |
| `-XX:+UseGraalJIT` (experimental) | JVMCI error | `Unrecognized VM option` (JVMCI hook removed) |
| `-Djdk.lang.Process.launchMechanism=VFORK` | deprecation banner | "has been removed. Switching to FORK instead" ([61](output/61-behaviour-changes.txt)) |
| `-Djava.locale.useOldISOCodes=true` | works, deprecated | ignored: `Locale("iw").language` is `he` ([61](output/61-behaviour-changes.txt)) |
**The point of the table:** rows 1-2 are the ones that stop a service from starting. They are old flags that live in Dockerfiles and `JAVA_TOOL_OPTIONS` long after anyone remembers why.
Grep your images before you upgrade.
## New public API worth knowing
Found by diffing `javap` dumps ([51](output/51-api-diff-26-to-27.txt)) and exercised once each in [`Api27`](../other-changes/src/Api27.java) ([62](output/62-new-api.txt)):
`Math.acosh/asinh/atanh`, `String.encodedLength(Charset)` (length in bytes without allocating the array), `BigDecimal.rootn`, `KeyStore.getCreationInstant`.
Prev: [9. JFR and Vector](09-jfr-redaction-and-vector.md) &middot; Next: [11. Recap of Java 26](11-recap-26.md)
+68
View File
@@ -0,0 +1,68 @@
# 11. Recap: the Java 26 changes you skipped
Prev: [10. Other 27 changes](10-other-27-changes.md) &middot; Next: [12. Version lanes 25 - 29](12-lanes-25-to-29.md)
Java 26 (GA 2026-03-17) is also a non-LTS release. If you are on 25 and going to 27 you meet all of it at once. Script: [`scripts/recap26.sh`](../scripts/recap26.sh).
## JEP 500: final means final
Reflection has always let `Field.setAccessible(true)` followed by `Field.setInt(...)` overwrite a `final` field. Source: [`FinalFieldMutation`](../recap26/src/FinalFieldMutation.java), transcript [70](output/70-final-field-mutation.txt).
| Run | Result |
|---|---|
| JDK 25 | silent |
| JDK 26 default | three `WARNING:` lines, then it proceeds |
| JDK 26 `--enable-final-field-mutation=ALL-UNNAMED` | silent (the opt-in the warning names) |
| JDK 26 `--illegal-final-field-mutation=deny` | `IllegalAccessException`: "... is not allowed to mutate final fields" |
| JDK 26 `--illegal-final-field-mutation=debug` | the warning plus the stack trace of the offending call |
`--illegal-final-field-mutation` takes `allow`, `warn`, `debug` or `deny`. A JFR event, `jdk.FinalFieldMutation`, is recorded with default settings whenever a mutation is *permitted*
(one event, with the declaring class and field name); under `deny` there is nothing to record.
Even when it works it misleads: the field reads 9090 but `port()` still returns 8080, because `final int port = 8080` is a constant variable and javac copied the literal into the method.
Who this hits: serialisation libraries, mocking frameworks, dependency-injection containers and test code that set `final` fields. Run your test suite once with `deny` on 26 or later.
## JEP 504: the Applet API is gone
`java.applet.*`, `javax.swing.JApplet` and the `AppletInitializer` overload of `java.beans.Beans.instantiate` were removed ([52](output/52-api-diff-25-to-26.txt)).
[`AppletGone.java`](../recap26/src/broken/AppletGone.java) compiles on 25 with a `[removal]` warning and fails on 26 with `package java.applet does not exist` ([73](output/73-removed-in-26.txt)).
In the same transcript, [`ThreadStopGone.java`](../recap26/src/broken/ThreadStopGone.java): `Thread.stop()` (which has thrown `UnsupportedOperationException` since 20) no longer exists at all: `cannot find symbol`.
## JEP 516: AOT object caching with any GC
The AOT cache (JEP 483 / 514 / 515 in 24 and 25) stores pre-parsed and pre-linked classes and, in some configurations, pre-built heap objects such as the module graph.
Until 26 the heap-object part needed G1. Workflow used here: `-XX:AOTMode=record -XX:AOTConfiguration=...`, then `-XX:AOTMode=create -XX:AOTCache=...`, then run with `-XX:AOTCache=...`.
What the JVM's own log says ([71](output/71-aot-cache-matrix.txt)):
| Cache built under | Run under | JDK 25 | JDK 26 / 27 |
|---|---|---|---|
| G1 | G1 or Serial | heap objects used | heap objects used |
| G1 | ZGC | cache refused | cache refused |
| ZGC | ZGC | cache used, **heap objects not used** | **heap objects used** |
| ZGC | G1 or Serial | cache refused | cache refused |
So "any GC" means *each* GC can now benefit, not that one cache serves every GC: the cache records whether compressed oops were on
(`The saved state of UseCompressedOops (1) is different from runtime (0), CDS will be disabled`), and ZGC does not use them. Build the cache under the collector you deploy with.
Startup, median of 9 runs, indicative only ([72](output/72-aot-startup.txt)): with ZGC, JDK 25 barely moves (about 83 -> 81 ms, because no heap objects are used), while JDK 26 and 27 go from about 62 to about 40 ms; with G1 all three go from about 60 to about 35-37 ms.
A 27 JVM has a new diagnostic flag `AOTCompatibleOopCompression`; setting it at cache creation did not make a G1-built cache load under ZGC in one attempt, and nothing more was investigated.
## JEP 517: HTTP/3 in the HTTP client
`HttpClient.Version.HTTP_3`, `HttpOption.H3_DISCOVERY` and `HttpOption.Http3DiscoveryMode` (`ANY`, `ALT_SVC`, `HTTP_3_URI_ONLY`) are the API surface ([75](output/75-http3-fallback.txt), from `javap`).
The sandbox has no UDP egress, so no real HTTP/3 exchange was run. What [`Http3Fallback`](../recap26/src/Http3Fallback.java) shows instead: a client that *prefers* HTTP/3 against a TLS server that only speaks HTTP/1.1
falls back and `response.version()` says `HTTP_1_1`; a request pinned to `HTTP_3_URI_ONLY` fails with `HttpConnectTimeoutException: quic handshake timeout`.
## JEP 522: G1 throughput
"Improve throughput by reducing synchronization between application and GC threads." Not benchmarked here; it is a claim from the JEP, and it is the reason 26 is a fairer G1 baseline than 25 in [chapter 2](02-g1-default.md).
## New public API without a JEP
Verified by javap diff ([52](output/52-api-diff-25-to-26.txt)) and asserted in [`Api26`](../recap26/src/Api26.java) ([74](output/74-api26.txt)): `String.equalsFoldCase`, `compareToFoldCase` and `UNICODE_CASEFOLD_ORDER`
(`"STRASSE".equalsFoldCase("straße")` is true; `equalsIgnoreCase` says false), `UUID.ofEpochMillis` (a version-7 UUID for a given instant), `Process` implementing `Closeable`,
`MemoryMXBean.getTotalGcCpuTime()`, `ByteOrder` becoming an enum, `HPKEParameterSpec` (Hybrid Public Key Encryption), and the Unicode 17 blocks in `Character.UnicodeBlock`.
Prev: [10. Other 27 changes](10-other-27-changes.md) &middot; Next: [12. Version lanes 25 - 29](12-lanes-25-to-29.md)
+31
View File
@@ -0,0 +1,31 @@
# 12. Version lanes: 25 (LTS) to 29 (next LTS)
Ordinals in the 25 column come from Java 25's published JEP list (470, 502, 505, 507, 508), not from a run here; the existence of `StableValue` and `PEMRecord` in 25 and their absence in 26 *was* observed ([52](output/52-api-diff-25-to-26.txt)).
Prev: [11. Recap of Java 26](11-recap-26.md) &middot; Next: [13. Upgrade checklist](13-upgrade-checklist.md) &middot; Coming from 21: [14](14-lanes-21-to-25.md)
Dates and support windows are from Oracle's Java SE Support Roadmap, read on 2026-09-21: <https://www.oracle.com/java/technologies/java-se-support-roadmap.html>.
Feature notes link to the chapter that has the run.
| Release | GA | Kind | Premier support until | What to read here |
|---|---|---|---|---|
| 25 | Sept 2025 | **LTS** | Sept 2030 | the baseline for every "broke on 26/27" demo in this repository |
| 26 | March 2026 | non-LTS | Sept 2026 | [chapter 11](11-recap-26.md): final-field warnings, Applet removal, HTTP/3, AOT with any GC |
| 27 | Sept 2026 | non-LTS | March 2027 | chapters [2](02-g1-default.md) - [10](10-other-27-changes.md): G1 default, compact headers, PQ TLS, JFR redaction |
| 28 | March 2027 | non-LTS (planned) | Sept 2027 | not covered: not released |
| 29 | Sept 2027 | **LTS (planned)** | Sept 2032 | not covered: not released |
Oracle's stated cadence: "Oracle intends to make future LTS releases every two years meaning the next planned LTS release is Java 29 in September 2027."
Other vendors set their own support lengths for non-LTS builds; check the vendor you deploy.
The preview-feature ladder across the lane (each preview must be recompiled and can break the next release, see [20-broken-*](output/)):
| Feature | 25 | 26 | 27 |
|---|---|---|---|
| Structured Concurrency | 5th preview (JEP 505); `--enable-preview` needed, run in [82](output/82-structured-not-final.txt) | 6th preview (JEP 525), `anySuccessfulResultOrThrow` renamed `anySuccessfulOrThrow` ([89](output/89-joiner-api.txt)) | 7th preview (JEP 533), API reshaped |
| Lazy Constants (was Stable Values in 25) | Stable Values, preview | 2nd preview (JEP 526) | 3rd preview (JEP 531), `orElse`/`isInitialized` removed |
| PEM encodings | 1st preview (`PEMRecord`) | 2nd preview (JEP 524) | 3rd preview (JEP 538), `DEREncodable` renamed |
| Primitive patterns | 3rd preview | 4th preview (JEP 530) | 5th preview (JEP 532) |
| Vector API | 10th incubator | 11th (JEP 529) | 12th (JEP 537) |
Prev: [11. Recap of Java 26](11-recap-26.md) &middot; Next: [13. Upgrade checklist](13-upgrade-checklist.md) &middot; Coming from 21: [14](14-lanes-21-to-25.md)
+18
View File
@@ -0,0 +1,18 @@
# 13. Upgrade checklist: 25 to 27 (or to 26)
Prev: [12. Version lanes](12-lanes-25-to-29.md) &middot; Next: [14. The 21 to 25 lane](14-lanes-21-to-25.md) &middot; [Back to the README](../README.md)
Ordered by "will stop a service from starting" first.
1. **Grep for flags that no longer exist.** `-noverify`, `-Xverify:none` and `-noclassgc` make a 27 JVM refuse to start ([60](output/60-removed-options.txt)). Look in Dockerfiles, `JAVA_TOOL_OPTIONS`, systemd units and start scripts.
2. **Pin your collector in small containers.** If nobody sets one, 27 gives you G1 on one CPU, where 26 gave you Serial ([02](02-g1-default.md)). Decide on purpose, and set `-Xmx` or `-XX:MaxRAMPercentage`.
3. **Run your tests once with `--illegal-final-field-mutation=deny` on 26 or later.** Mocking, DI and serialisation libraries are the usual offenders ([11](11-recap-26.md)).
4. **Compile everything on 26 and 27.** `java.applet`, `JApplet` and `Thread.stop()` are gone ([73](output/73-removed-in-26.txt)).
5. **Test TLS through your real network path.** JDK 27 clients send a 1573-byte ClientHello ([04](04-post-quantum-tls.md)). If a middlebox objects, `-Djdk.tls.namedGroups=x25519,secp256r1` restores the old behaviour.
6. **Search for hard-coded object sizes.** Estimators, capacity plans and tests that assume a 12-byte header ([03](03-compact-headers.md)).
7. **Do not ship preview APIs from a library.** Structured concurrency, lazy constants and PEM changed API between 26 and 27, and primitive patterns is now on its fifth preview ([12](12-lanes-25-to-29.md)).
8. **Check custom JFR redaction settings.** A list without `+` replaces the defaults ([09](09-jfr-redaction-and-vector.md)).
9. **Rebuild AOT caches per JDK and per collector** ([11](11-recap-26.md)).
10. **Decide whether to move at all.** 26 and 27 are non-LTS: 26's premier support ends September 2026 and 27's in March 2027. Staying on 25 and planning for 29 is a legitimate answer ([12](12-lanes-25-to-29.md)).
Prev: [12. Version lanes](12-lanes-25-to-29.md) &middot; [Back to the README](../README.md)
+40
View File
@@ -0,0 +1,40 @@
# 14. The 21 to 25 lane: every claim in the hub posts, checked
Prev: [13. Upgrade checklist](13-upgrade-checklist.md) &middot; [Back to the README](../README.md)
The two Java version guides on ankurm.com ([features](https://ankurm.com/java-21-to-25-lts-features/), [upgrade guide](https://ankurm.com/java-21-to-25-upgrade-guide-ai-prompts/))
make about twenty checkable claims. `scripts/lanes.sh` checks each one on JDK 21, 25, 26 and 27, plus 23 and 24 (from the `amazoncorretto:23` and `:24`
images) where the question is *when* something changed. The verdicts are below; the transcripts are the proof.
| Claim | Verdict | Where it changed | Transcript |
|---|---|---|---|
| Records, pattern-matching `switch`, virtual threads, sequenced collections from the article run unchanged on 21, 25, 26, 27 | true | final in 21 | [80](output/80-hub-examples.txt) |
| Scoped values are final in 25 | true | preview in 21, final in 25 (JEP 506) | [81](output/81-scoped-values.txt) |
| Structured concurrency is final in 25 | **false: still preview** | 5th preview in 25, 6th in 26, 7th in 27; `--enable-preview` is required on all three | [82](output/82-structured-not-final.txt) |
| `Joiner.anySuccessfulResultOrThrow()` (the article's code) works on 26 and 27 | **false: renamed** | `anySuccessfulOrThrow()` from 26; `allSuccessfulOrThrow()` returns a `List` instead of a `Stream` from 26; a third type parameter arrives in 27 | [82](output/82-structured-not-final.txt), [89](output/89-joiner-api.txt) |
| Finalization is removed by default | **false: still on** | `finalize()` still runs on 21, 25, 26 and 27 unless you pass `--finalization=disabled` | [83](output/83-finalization.txt) |
| `System.setSecurityManager()` throws `UnsupportedOperationException` | true | already on 21 by default; permanent from 24 (JEP 486) | [84](output/84-security-manager.txt) |
| `Thread.stop`, `suspend` and `resume` are removed | **half true** | `suspend`/`resume` compile on 21 and do not compile on 23, 24, 25 or 26; `stop()` still compiles on 25 and throws; removed in 26 | [85](output/85-thread-stop-suspend.txt), [73](output/73-removed-in-26.txt) |
| `sun.misc.Unsafe` memory methods warn | true from 24 | none on 21, warning on 24 and 25 (JEP 498) | [86](output/86-unsafe-and-jni.txt) |
| JNI use warns by default | true from 24 | none on 21, warning on 24 and 25 (JEP 472) | [86](output/86-unsafe-and-jni.txt) |
| Virtual threads pin their carrier inside `synchronized` on "Java 21 to 24" | **false: 21 to 23** | pinned on 21 and 23, not pinned on 24, 25, 26, 27 (JEP 491) | [87](output/87-pinning.txt) |
| "Java 25 ships an improved generational ZGC mode" | **misleading** | generational is the only ZGC from 24; `-XX:-ZGenerational` is ignored with a warning on 24 and 25 and rejected on 26 and 27 | [88](output/88-zgc-generational.txt) |
| Reflection writing to a `final` field warns from 26 | true | silent on 25, warns on 26 and 27 (JEP 500) | [70](output/70-final-field-mutation.txt), [91](output/91-final-field-mutation-27.txt) |
| Unnamed patterns and unnamed classes are final in 25 | **half true** | the underscore was final in 22 (JEP 456); compact source files with instance `main()` (JEP 512) are final in 25 | [90](output/90-unnamed-and-compact.txt) |
Build-file claims (`--release`, Gradle, Lombok, Mockito, annotation processing, `jdeps`) are checked in [chapter 15](15-build-files.md).
Claims about other projects came from their own pages, read on 2026-09-21, not from a run:
| Claim | Verdict | Source |
|---|---|---|
| Spring Boot 3.4 is the first with official Java 25 support | **false** | Spring Boot 3.4.x is documented up to Java 24, 3.5.x up to Java 25, 4.1.1 up to Java 26 ([system requirements](https://docs.spring.io/spring-boot/system-requirements.html)); 27 is not listed yet |
| Lombok 1.18.34 or later works on Java 25 | **false** | JDK 25 support arrived in 1.18.40 (2025-09-04), JDK 26 in 1.18.46, JDK 27 in 1.18.48 ([changelog](https://projectlombok.org/changelog)); a run confirms 1.18.34 fails on 25 ([95](output/95-lombok-matrix.txt), [chapter 15](15-build-files.md)) |
| Mockito 5.12 or later is enough for Java 25 | **false** (run) | 5.12.0 and 5.17.0 cannot mock an interface on 25 or 27; 5.18.0 is the first that can ([97](output/97-mockito-bytebuddy.txt), [chapter 15](15-build-files.md)) |
| Hibernate 7 requires Jakarta EE 10 | **false** | Hibernate ORM 7.0 requires Jakarta Persistence 3.2 (Jakarta EE 11) ([release page](https://hibernate.org/orm/releases/7.0/)) |
The next chapter is [15](15-build-files.md), the build-file half; [12](12-lanes-25-to-29.md) carries the lane table forward to 29.
`prompts/` holds the thirteen AI prompts from the two posts, one per file, corrected to match this chapter.
Prev: [13. Upgrade checklist](13-upgrade-checklist.md) &middot; [Back to the README](../README.md)
+61
View File
@@ -0,0 +1,61 @@
# 15. Build files: what the 21 to 25 upgrade does to Maven, Gradle and Lombok
Prev: [14. The 21 to 25 lane](14-lanes-21-to-25.md) &middot; [Back to the README](../README.md)
The build half of the [upgrade guide](https://ankurm.com/java-21-to-25-upgrade-guide-ai-prompts/), run by `scripts/upgrade.sh`.
Everything below is a run on JDK 21, 23, 24, 25, 26 or 27; the transcripts are in `output/92` to `output/97`.
## 15.1 Maven: `maven.compiler.release`
[`lanes/upgrade/pom.xml`](../lanes/upgrade/pom.xml) sets `maven.compiler.source`, `target` and `release` to 25.
* Building on **JDK 21** fails at once: `error: release version 25 not supported`. The build JDK has to be 25 before the release can be 25 ([92](output/92-maven-release.txt)).
* Building on **JDK 25** succeeds and the class file has major version 69. Building on **JDK 27** with `release` still 25 also succeeds and also produces version 69: a newer JDK can compile for 25. Running that class on JDK 21 gives `UnsupportedClassVersionError` (65 is Java 21).
* `--release N` restricts the compiler to the *API of release N*, not just the language level and byte-code version. [94](output/94-release-flag.txt): JDK 27's `javac --release 25` rejects `String.equalsFoldCase` (new in 26).
`javac -source 25 -target 25` compiles it with a warning, and the class then dies on a JDK 25 with `NoSuchMethodError`. That, and not "rejecting internal APIs", is what `--release` buys you.
## 15.2 Gradle: the toolchain block, and the JDK that runs Gradle
[`lanes/upgrade/build.gradle.kts`](../lanes/upgrade/build.gradle.kts) asks for a Java 25 toolchain. Two different JDKs are involved ([93](output/93-gradle-toolchain.txt)):
* the toolchain JDK, which compiles your code: Gradle 8.14.3 found and used JDK 25 for this while itself running on JDK 21;
* the JDK that runs Gradle itself: Gradle 8.14.3 on JDK 25 fails (`25.0.4.1` with a Kotlin DSL script; `Unsupported class file major version 69` with a Groovy one, seen in a side run), Gradle 9.1.0 on JDK 25 works.
Gradle's [compatibility table](https://docs.gradle.org/current/userguide/compatibility.html), read 2026-09-21: Java 25 needs Gradle 9.1.0, Java 26 needs 9.4.0, and Java 27 is not listed for any release yet.
## 15.3 Lombok and annotation processing
[`lanes/src/LombokProbe.java`](../lanes/src/LombokProbe.java) needs Lombok's processor to compile. [95](output/95-lombok-matrix.txt):
| Lombok | JDK 25 | JDK 26 | JDK 27 |
|---|---|---|---|
| 1.18.34 | fails: `ExceptionInInitializerError` | fails | fails |
| 1.18.40 | compiles | compiles | fails |
| 1.18.46 | compiles | compiles | fails |
| 1.18.48 | compiles | compiles | compiles |
The probe uses one annotation, so "compiles" is a lower bound: the [Lombok changelog](https://projectlombok.org/changelog) is the support statement (JDK 25 in 1.18.40, JDK 26 in 1.18.46, JDK 27 in 1.18.48).
1.18.40 compiling on 26 does not contradict it.
Separate trap, same file: since JDK 23 `javac` no longer runs annotation processors it merely finds on the class path. JDK 21 compiles the probe with a `Note: Annotation processing is enabled...`; JDK 23, 24 and 25 give `cannot find symbol` for `getName()`.
Maven and Gradle projects that declare the processor with `annotationProcessorPaths` / `annotationProcessor` are unaffected; hand-run `javac` lines and IDE setups that relied on discovery are not.
## 15.4 Mockito and ByteBuddy
[`lanes/src/MockitoProbe.java`](../lanes/src/MockitoProbe.java) mocks an interface and stubs one call. [97](output/97-mockito-bytebuddy.txt), each Mockito with the ByteBuddy it declares:
| Mockito | ByteBuddy | JDK 21 | JDK 25 | JDK 27 |
|---|---|---|---|---|
| 5.12.0 | 1.14.15 | works | fails | fails |
| 5.17.0 | 1.15.11 | works | fails | fails |
| 5.18.0 | 1.17.5 | works | works | works |
| 5.23.0 | 1.17.7 | works | works | works |
The failure is `Mockito cannot mock this class` (ByteBuddy cannot instrument class files newer than it knows). 5.18.0 was the first version that worked; 5.14.2, 5.16.1 and 5.17.0 failed in a side run (not committed).
Even on the working versions the JVM prints `WARNING: A Java agent has been loaded dynamically` on 21, 25 and 27, and Mockito 5.23.0 adds a note that self-attaching will stop working: the fix is to add Mockito as an agent, as its documentation describes.
## 15.5 `jdeps --jdk-internals`
[96](output/96-jdeps-internals.txt) is `jdeps --jdk-internals` on the class from [`UnsafeWarning.java`](../lanes/src/UnsafeWarning.java): it names `sun.misc.Unsafe` before the JVM prints its run-time warning ([86](output/86-unsafe-and-jni.txt)).
Prev: [14. The 21 to 25 lane](14-lanes-21-to-25.md) &middot; [Back to the README](../README.md)
@@ -0,0 +1,43 @@
# JEP 523: which collector does the JVM pick when you do not choose one?
# Same class files, same vendor (Amazon Corretto), same 2 GB memory limit. Only the JDK version and the CPU limit change.
=== amazoncorretto:26 --cpus=1 --memory=2g
$ docker run --rm --cpus=1 --memory=2g amazoncorretto:26 java -cp /app GcReport
java.version = 26.0.2.1
availableCpus = 1
maxHeapMB = 511
collector beans = Copy, MarkSweepCompact
UseG1GC = false (origin DEFAULT)
UseSerialGC = true (origin ERGONOMIC)
UseParallelGC = false (origin DEFAULT)
=== amazoncorretto:27 --cpus=1 --memory=2g
$ docker run --rm --cpus=1 --memory=2g amazoncorretto:27 java -cp /app GcReport
java.version = 27
availableCpus = 1
maxHeapMB = 512
collector beans = G1 Young Generation, G1 Concurrent GC, G1 Old Generation
UseG1GC = true (origin ERGONOMIC)
UseSerialGC = false (origin DEFAULT)
UseParallelGC = false (origin DEFAULT)
=== amazoncorretto:26 --cpus=2 --memory=2g
$ docker run --rm --cpus=2 --memory=2g amazoncorretto:26 java -cp /app GcReport
java.version = 26.0.2.1
availableCpus = 2
maxHeapMB = 512
collector beans = G1 Young Generation, G1 Concurrent GC, G1 Old Generation
UseG1GC = true (origin ERGONOMIC)
UseSerialGC = false (origin DEFAULT)
UseParallelGC = false (origin DEFAULT)
=== amazoncorretto:27 --cpus=2 --memory=2g
$ docker run --rm --cpus=2 --memory=2g amazoncorretto:27 java -cp /app GcReport
java.version = 27
availableCpus = 2
maxHeapMB = 512
collector beans = G1 Young Generation, G1 Concurrent GC, G1 Old Generation
UseG1GC = true (origin ERGONOMIC)
UseSerialGC = false (origin DEFAULT)
UseParallelGC = false (origin DEFAULT)
+43
View File
@@ -0,0 +1,43 @@
# The other half of the old rule: memory. The old heuristic wanted at least 2 CPUs AND about 1792 MB.
# With 2 CPUs the CPU half of the rule is satisfied, so only the memory limit differs between these runs.
=== amazoncorretto:26 --cpus=2 --memory=1g
$ docker run --rm --cpus=2 --memory=1g amazoncorretto:26 java -cp /app GcReport
java.version = 26.0.2.1
availableCpus = 2
maxHeapMB = 255
collector beans = Copy, MarkSweepCompact
UseG1GC = false (origin DEFAULT)
UseSerialGC = true (origin ERGONOMIC)
UseParallelGC = false (origin DEFAULT)
=== amazoncorretto:27 --cpus=2 --memory=1g
$ docker run --rm --cpus=2 --memory=1g amazoncorretto:27 java -cp /app GcReport
java.version = 27
availableCpus = 2
maxHeapMB = 256
collector beans = G1 Young Generation, G1 Concurrent GC, G1 Old Generation
UseG1GC = true (origin ERGONOMIC)
UseSerialGC = false (origin DEFAULT)
UseParallelGC = false (origin DEFAULT)
=== amazoncorretto:26 --cpus=2 --memory=2g
$ docker run --rm --cpus=2 --memory=2g amazoncorretto:26 java -cp /app GcReport
java.version = 26.0.2.1
availableCpus = 2
maxHeapMB = 512
collector beans = G1 Young Generation, G1 Concurrent GC, G1 Old Generation
UseG1GC = true (origin ERGONOMIC)
UseSerialGC = false (origin DEFAULT)
UseParallelGC = false (origin DEFAULT)
=== amazoncorretto:27 --cpus=2 --memory=2g
$ docker run --rm --cpus=2 --memory=2g amazoncorretto:27 java -cp /app GcReport
java.version = 27
availableCpus = 2
maxHeapMB = 512
collector beans = G1 Young Generation, G1 Concurrent GC, G1 Old Generation
UseG1GC = true (origin ERGONOMIC)
UseSerialGC = false (origin DEFAULT)
UseParallelGC = false (origin DEFAULT)
+11
View File
@@ -0,0 +1,11 @@
# The opt-out still works on 27: an explicit choice always wins over the JVM's.
=== amazoncorretto:27 --cpus=1 --memory=2g -XX:+UseSerialGC
$ docker run --rm --cpus=1 --memory=2g amazoncorretto:27 java -XX:+UseSerialGC -cp /app GcReport
java.version = 27
availableCpus = 1
maxHeapMB = 511
collector beans = Copy, MarkSweepCompact
UseG1GC = false (origin DEFAULT)
UseSerialGC = true (origin VM_CREATION)
UseParallelGC = false (origin DEFAULT)
+51
View File
@@ -0,0 +1,51 @@
# Allocation workload, 1 CPU / 2 GB container, 4000 batches x 50,000 objects, 3 runs per configuration (indicative, not a benchmark)
# 'default' means nobody passed a collector flag.
=== amazoncorretto:26 default run 1
wall time = 5980 ms
gc count / time = 1275 / 1831 ms
slowest batch = 115 ms
peak RSS = 557 MB
=== amazoncorretto:26 default run 2
wall time = 6145 ms
gc count / time = 1275 / 1872 ms
slowest batch = 142 ms
peak RSS = 557 MB
=== amazoncorretto:26 default run 3
wall time = 6402 ms
gc count / time = 1275 / 1861 ms
slowest batch = 129 ms
peak RSS = 557 MB
=== amazoncorretto:27 default run 1
wall time = 8196 ms
gc count / time = 140 / 1955 ms
slowest batch = 39 ms
peak RSS = 547 MB
=== amazoncorretto:27 default run 2
wall time = 8214 ms
gc count / time = 140 / 1970 ms
slowest batch = 41 ms
peak RSS = 547 MB
=== amazoncorretto:27 default run 3
wall time = 8432 ms
gc count / time = 140 / 2004 ms
slowest batch = 35 ms
peak RSS = 546 MB
=== amazoncorretto:27 -XX:+UseSerialGC run 1
wall time = 5306 ms
gc count / time = 1264 / 1669 ms
slowest batch = 125 ms
peak RSS = 557 MB
=== amazoncorretto:27 -XX:+UseSerialGC run 2
wall time = 4972 ms
gc count / time = 1264 / 1599 ms
slowest batch = 119 ms
peak RSS = 557 MB
=== amazoncorretto:27 -XX:+UseSerialGC run 3
wall time = 5698 ms
gc count / time = 1264 / 1713 ms
slowest batch = 128 ms
peak RSS = 557 MB
+51
View File
@@ -0,0 +1,51 @@
# Allocation workload, 1 CPU / 2 GB container, 400 batches x 50,000 objects, 3 runs per configuration (indicative, not a benchmark)
# 'default' means nobody passed a collector flag.
=== amazoncorretto:26 default run 1
wall time = 531 ms
gc count / time = 683 / 160 ms
slowest batch = 25 ms
peak RSS = 102 MB
=== amazoncorretto:26 default run 2
wall time = 536 ms
gc count / time = 683 / 165 ms
slowest batch = 22 ms
peak RSS = 103 MB
=== amazoncorretto:26 default run 3
wall time = 564 ms
gc count / time = 683 / 161 ms
slowest batch = 36 ms
peak RSS = 102 MB
=== amazoncorretto:27 default run 1
wall time = 1052 ms
gc count / time = 26 / 204 ms
slowest batch = 26 ms
peak RSS = 492 MB
=== amazoncorretto:27 default run 2
wall time = 974 ms
gc count / time = 26 / 192 ms
slowest batch = 25 ms
peak RSS = 492 MB
=== amazoncorretto:27 default run 3
wall time = 1041 ms
gc count / time = 26 / 234 ms
slowest batch = 36 ms
peak RSS = 492 MB
=== amazoncorretto:27 -XX:+UseSerialGC run 1
wall time = 528 ms
gc count / time = 668 / 147 ms
slowest batch = 23 ms
peak RSS = 98 MB
=== amazoncorretto:27 -XX:+UseSerialGC run 2
wall time = 522 ms
gc count / time = 668 / 157 ms
slowest batch = 27 ms
peak RSS = 98 MB
=== amazoncorretto:27 -XX:+UseSerialGC run 3
wall time = 588 ms
gc count / time = 668 / 157 ms
slowest batch = 48 ms
peak RSS = 97 MB
+41
View File
@@ -0,0 +1,41 @@
# JDK 27, G1 (the default), 1 CPU / 2 GB container, 400 batches, 2 runs per configuration
=== flags that differ between 26 and 27 (from -XX:+PrintFlagsFinal)
--- amazoncorretto:26
uint ConcGCThreads = 0 {product} {default}
uintx MaxHeapFreeRatio = 70 {manageable} {default}
size_t MaxHeapSize = 536870912 {product} {ergonomic}
uintx MinHeapFreeRatio = 40 {manageable} {default}
uint ParallelGCThreads = 0 {product} {default}
bool UseCompactObjectHeaders = false {product lp64_product} {default}
bool UseCompressedClassPointers = true {product} {default}
--- amazoncorretto:27
uint ConcGCThreads = 1 {product} {ergonomic}
uint G1IHOP = 45 {product} {default}
uintx MaxHeapFreeRatio = 100 {manageable} {default}
size_t MaxHeapSize = 536870912 {product} {ergonomic}
uintx MinHeapFreeRatio = 0 {manageable} {default}
uint ParallelGCThreads = 1 {product} {default}
bool UseCompactObjectHeaders = true {product lp64_product} {default}
=== amazoncorretto:27 flags: [none]
wall time = 1064 ms
gc count / time = 26 / 208 ms
peak RSS = 492 MB
wall time = 1078 ms
gc count / time = 26 / 202 ms
peak RSS = 493 MB
=== amazoncorretto:27 flags: [-XX:MinHeapFreeRatio=40 -XX:MaxHeapFreeRatio=70]
wall time = 1115 ms
gc count / time = 26 / 268 ms
peak RSS = 492 MB
wall time = 982 ms
gc count / time = 26 / 199 ms
peak RSS = 492 MB
=== amazoncorretto:27 flags: [-Xmx256m]
wall time = 976 ms
gc count / time = 37 / 240 ms
peak RSS = 294 MB
wall time = 957 ms
gc count / time = 37 / 261 ms
peak RSS = 294 MB
+82
View File
@@ -0,0 +1,82 @@
java.version = 26.0.2.1
# VM mode: 64 bits
# Compressed references (oops): 0-bit shift
# Compressed class pointers: 0-bit shift and 0x3E000000 base
# Object alignment: 8 bytes
# ref, bool, byte, char, shrt, int, flt, lng, dbl
# Field sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8
# Array base offsets: 16, 16, 16, 16, 16, 16, 16, 16, 16
=== java.lang.Object
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
=== Point { int x; int y; }
HeaderDemo$Point object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 int Point.x N/A
16 4 int Point.y N/A
20 4 (object alignment gap)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
=== Order { long id; int quantity; boolean paid; String customer; }
HeaderDemo$Order object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 int Order.quantity N/A
16 8 long Order.id N/A
24 1 boolean Order.paid N/A
25 3 (alignment/padding gap)
28 4 java.lang.String Order.customer N/A
Instance size: 32 bytes
Space losses: 3 bytes internal + 0 bytes external = 3 bytes total
=== java.lang.Long and java.lang.Integer (boxed values)
java.lang.Long object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x001a15d0
12 4 (alignment/padding gap)
16 8 long Long.value 123456789
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
java.lang.Integer object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x0019f570
12 4 int Integer.value 123456
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
=== int[3]
[I object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x00174360
12 4 (array length) 3
16 12 int [I.<elements> N/A
28 4 (object alignment gap)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
=== What it adds up to: one million objects
1,000,000 boxed Integer values in an ArrayList : 20,861,992 bytes
1,000,000 boxed Long values in an ArrayList : 28,861,992 bytes
1,000,000 Point objects in an ArrayList : 28,861,992 bytes
+79
View File
@@ -0,0 +1,79 @@
java.version = 27
# WARNING: Unable to attach Serviceability Agent. sun.jvm.hotspot.runtime.VM.isCompressedKlassPointersEnabled()
# VM mode: 64 bits
# Lilliput VM detected (experimental)
# Compressed references (oops): 3-bit shift
# Compressed class pointers: 3-bit shift
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Object alignment: 8 bytes
# ref, bool, byte, char, shrt, int, flt, lng, dbl
# Field sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8
# Array base offsets: 12, 12, 12, 12, 12, 12, 12, 16, 16
=== java.lang.Object
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
Instance size: 8 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
=== Point { int x; int y; }
HeaderDemo$Point object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
8 4 int Point.x N/A
12 4 int Point.y N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
=== Order { long id; int quantity; boolean paid; String customer; }
HeaderDemo$Order object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
8 8 long Order.id N/A
16 4 int Order.quantity N/A
20 1 boolean Order.paid N/A
21 3 (alignment/padding gap)
24 4 java.lang.String Order.customer N/A
28 4 (object alignment gap)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total
=== java.lang.Long and java.lang.Integer (boxed values)
java.lang.Long object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x001aa80000000001 (Lilliput)
8 8 long Long.value 123456789
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
java.lang.Integer object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x001a840000000001 (Lilliput)
8 4 int Integer.value 123456
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
=== int[3]
[I object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0017840000000001 (Lilliput)
8 4 (array length) 3
12 12 int [I.<elements> N/A
Instance size: 24 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
=== What it adds up to: one million objects
1,000,000 boxed Integer values in an ArrayList : 20,861,984 bytes
1,000,000 boxed Long values in an ArrayList : 20,861,984 bytes
1,000,000 Point objects in an ArrayList : 20,861,984 bytes
+86
View File
@@ -0,0 +1,86 @@
java.version = 27
# WARNING: Unable to attach Serviceability Agent. sun.jvm.hotspot.runtime.VM.isCompressedKlassPointersEnabled()
# VM mode: 64 bits
# Compressed references (oops): 3-bit shift
# Compressed class pointers: 3-bit shift
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Object alignment: 8 bytes
# ref, bool, byte, char, shrt, int, flt, lng, dbl
# Field sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8
# Array base offsets: 16, 16, 16, 16, 16, 16, 16, 16, 16
=== java.lang.Object
java.lang.Object object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 (object alignment gap)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
=== Point { int x; int y; }
HeaderDemo$Point object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 int Point.x N/A
16 4 int Point.y N/A
20 4 (object alignment gap)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
=== Order { long id; int quantity; boolean paid; String customer; }
HeaderDemo$Order object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
8 4 (object header: class) N/A
12 4 int Order.quantity N/A
16 8 long Order.id N/A
24 1 boolean Order.paid N/A
25 3 (alignment/padding gap)
28 4 java.lang.String Order.customer N/A
Instance size: 32 bytes
Space losses: 3 bytes internal + 0 bytes external = 3 bytes total
=== java.lang.Long and java.lang.Integer (boxed values)
java.lang.Long object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000009 (non-biasable; age: 1)
8 4 (object header: class) 0x001aa208
12 4 (alignment/padding gap)
16 8 long Long.value 123456789
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
java.lang.Integer object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x001a7db8
12 4 int Integer.value 123456
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
=== int[3]
[I object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)
8 4 (object header: class) 0x00176c00
12 4 (array length) 3
16 12 int [I.<elements> N/A
28 4 (object alignment gap)
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
=== What it adds up to: one million objects
1,000,000 boxed Integer values in an ArrayList : 20,861,992 bytes
1,000,000 boxed Long values in an ArrayList : 28,861,992 bytes
1,000,000 Point objects in an ArrayList : 28,861,992 bytes
+11
View File
@@ -0,0 +1,11 @@
# WARNING: Unable to attach Serviceability Agent. sun.jvm.hotspot.runtime.VM.isCompressedKlassPointersEnabled()
Exception in thread "main" java.lang.RuntimeException: Cannot get the field offset, try with -Djol.magicFieldOffset=true
at org.openjdk.jol.vm.HotspotUnsafe.fieldOffset(HotspotUnsafe.java:533)
at org.openjdk.jol.layouters.CurrentLayouter.layout(CurrentLayouter.java:70)
at org.openjdk.jol.info.ClassLayout.parseClass(ClassLayout.java:72)
at org.openjdk.jol.info.ClassLayout.parseClass(ClassLayout.java:56)
at RecordProbe.main(RecordProbe.java:12)
Caused by: java.lang.UnsupportedOperationException: can't get field offset on a record class: private final int RecordProbe$Coordinates.lat
at jdk.unsupported/sun.misc.Unsafe.objectFieldOffset(Unsafe.java:903)
at org.openjdk.jol.vm.HotspotUnsafe.fieldOffset(HotspotUnsafe.java:526)
... 4 more
+9
View File
@@ -0,0 +1,9 @@
# WARNING: Unable to attach Serviceability Agent. sun.jvm.hotspot.runtime.VM.isCompressedKlassPointersEnabled()
RecordProbe$Coordinates object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) N/A
8 4 int Coordinates.lat N/A
12 4 int Coordinates.lon N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
+31
View File
@@ -0,0 +1,31 @@
# Instance sizes in bytes, extracted from 10-, 11- and 12- transcripts
--- 10-headers-jdk26
=== java.lang.Object -> Instance size: 16 bytes
=== Point { int x; int y; } -> Instance size: 24 bytes
=== Order { long id; int quantity; boolean paid; String customer; } -> Instance size: 32 bytes
=== java.lang.Long and java.lang.Integer (boxed values) -> Instance size: 24 bytes
=== java.lang.Long and java.lang.Integer (boxed values) -> Instance size: 16 bytes
=== int[3] -> Instance size: 32 bytes
1,000,000 boxed Integer values in an ArrayList : 20,861,992 bytes
1,000,000 boxed Long values in an ArrayList : 28,861,992 bytes
1,000,000 Point objects in an ArrayList : 28,861,992 bytes
--- 11-headers-jdk27
=== java.lang.Object -> Instance size: 8 bytes
=== Point { int x; int y; } -> Instance size: 16 bytes
=== Order { long id; int quantity; boolean paid; String customer; } -> Instance size: 32 bytes
=== java.lang.Long and java.lang.Integer (boxed values) -> Instance size: 16 bytes
=== java.lang.Long and java.lang.Integer (boxed values) -> Instance size: 16 bytes
=== int[3] -> Instance size: 24 bytes
1,000,000 boxed Integer values in an ArrayList : 20,861,984 bytes
1,000,000 boxed Long values in an ArrayList : 20,861,984 bytes
1,000,000 Point objects in an ArrayList : 20,861,984 bytes
--- 12-headers-jdk27-opt-out
=== java.lang.Object -> Instance size: 16 bytes
=== Point { int x; int y; } -> Instance size: 24 bytes
=== Order { long id; int quantity; boolean paid; String customer; } -> Instance size: 32 bytes
=== java.lang.Long and java.lang.Integer (boxed values) -> Instance size: 24 bytes
=== java.lang.Long and java.lang.Integer (boxed values) -> Instance size: 16 bytes
=== int[3] -> Instance size: 32 bytes
1,000,000 boxed Integer values in an ArrayList : 20,861,992 bytes
1,000,000 boxed Long values in an ArrayList : 28,861,992 bytes
1,000,000 Point objects in an ArrayList : 28,861,992 bytes
+6
View File
@@ -0,0 +1,6 @@
$ javac --enable-preview --release 27 Dominated.java (JDK 27)
Dominated.java:9: error: this case label is dominated by a preceding case label
case int primitive -> "an int"; // dominated: every Integer is already caught above
^
1 error
exit=1
+16
View File
@@ -0,0 +1,16 @@
$ javac --enable-preview --release 26 Lazy26.java (JDK 26)
exit=0
$ javac --enable-preview --release 27 Lazy26.java (JDK 27)
Lazy26.java:12: error: cannot find symbol
if (!GREETING.isInitialized()) {
^
symbol: method isInitialized()
location: variable GREETING of type LazyConstant<String>
Lazy26.java:13: error: cannot find symbol
System.out.println("not yet: " + GREETING.orElse("<unset>"));
^
symbol: method orElse(String)
location: variable GREETING of type LazyConstant<String>
2 errors
exit=1
+19
View File
@@ -0,0 +1,19 @@
$ javac --enable-preview --release 26 Pem26Style.java (JDK 26)
exit=0
$ javac --enable-preview --release 27 Pem26Style.java (JDK 27)
Pem26Style.java:1: error: cannot find symbol
import java.security.DEREncodable;
^
symbol: class DEREncodable
location: package java.security
Pem26Style.java:20: error: cannot find symbol
PEMDecoder viaProvider = PEMDecoder.of().withFactory(provider); // 2
^
symbol: method withFactory(Provider)
location: class PEMDecoder
Pem26Style.java:22: error: incompatible types: byte[] cannot be converted to String
String base64Text = pem.content(); // 3
^
3 errors
exit=1
@@ -0,0 +1,19 @@
$ javac --enable-preview --release 26 StructuredScope26.java (JDK 26)
exit=0
$ javac --enable-preview --release 27 StructuredScope26.java (JDK 27)
StructuredScope26.java:12: error: wrong number of type arguments; required 3
try (StructuredTaskScope<String, List<String>> scope =
^
StructuredScope26.java:17: error: cannot find symbol
} catch (StructuredTaskScope.FailedException e) {
^
symbol: class FailedException
location: interface StructuredTaskScope
StructuredScope26.java:19: error: cannot find symbol
} catch (StructuredTaskScope.TimeoutException e) {
^
symbol: class TimeoutException
location: interface StructuredTaskScope
3 errors
exit=1
+14
View File
@@ -0,0 +1,14 @@
$ java --enable-preview StructuredDemo (JDK 27)
--- allSuccessfulOrThrow(): results as a List, failures as ExecutionException
results = [price=42, stock=7]
CHECK ok : join() returns the subtask results in fork order
caught java.util.concurrent.ExecutionException with cause java.lang.IllegalStateException: inventory service is down
CHECK ok : a failing subtask surfaces as ExecutionException(cause)
--- allSuccessfulOrThrow(Function): join() throws the exception type you choose
caught OrderFailed: order failed: inventory service is down
CHECK ok : the mapped exception wraps the original failure
--- withTimeout(...): what happens when the scope runs out of time
caught java.util.concurrent.ExecutionException
cause: java.util.concurrent.StructuredTaskScope$CancelledByTimeoutException
CHECK ok : a timeout is reported as CancelledByTimeoutException
exit=0
+22
View File
@@ -0,0 +1,22 @@
$ java --enable-preview LazyDemo (JDK 27)
--- a lazy constant computes once, on first use
toString before first get(): jdk.internal.lang.LazyConstantImpl@...[computing function=LazyDemo$$Lambda/0x...@...]
(loading settings on main)
toString after first get(): jdk.internal.lang.LazyConstantImpl@...[Settings[region=ap-south-1, poolSize=16]]
CHECK ok : get() returns the same instance every time
CHECK ok : the supplier ran exactly once
--- and once even when 64 threads race for it
CHECK ok : 64 racing threads triggered exactly one initialisation
--- List.ofLazy: each element is computed on first access
computed so far: 0
squares.get(3) = square(3)=9
squares.get(3) = square(3)=9 (second read)
CHECK ok : only element 3 was ever computed
--- Map.ofLazy: the keys are fixed, the values are computed on first access
lengths.get("gamma") = 5
CHECK ok : only the requested key's value was computed
--- Set.ofLazy (new in 27): membership is decided by a predicate, on demand
primes.contains(7) = true, primes.contains(6) = false
CHECK ok : the lazy set answers membership using the predicate
primes = [2, 3, 5, 7]
exit=0
+28
View File
@@ -0,0 +1,28 @@
$ java --enable-preview PrimitivePatterns (JDK 27)
--- instanceof <primitive>: an exact-conversion test, not a cast
100 instanceof byte = true
300 instanceof byte = false (a byte holds -128..127)
CHECK ok : 100 converts to byte exactly, 300 does not
binding: byte narrow = 100
16_777_216 instanceof float = true
16_777_217 instanceof float = false (float has a 24-bit significand)
CHECK ok : int to float is exact up to 2^24 and lossy after
--- switch on a long, with pattern labels and a guard
classify(0) = zero
classify(1) = one
classify(-7) = negative (-7)
classify(9000000000) = positive (9000000000)
CHECK ok : long switch dispatches on constants, then patterns
--- switch on an Object: an Integer matches 'case int'
describe(Integer 7) = an int 7
describe(Integer 5000) = a large int 5000
describe(Long 7) = a long 7
describe(Double 2.5) = a double 2.5
describe(String hello) = a String of length 5
describe(Character x) = something else: x
CHECK ok : an Integer 5000 matches case int with a guard
--- record pattern with a narrowing primitive component
Reading[celsius=36] -> fits in a byte: 36
Reading[celsius=4000] -> needs a full int: 4000
CHECK ok : Reading(byte c) matches 36 but not 4000
exit=0
+29
View File
@@ -0,0 +1,29 @@
$ java --enable-preview PemDemo (JDK 27)
--- public key, encoded
-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----
--- private key, encoded
-----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----
CHECK ok : a PublicKey encodes as BEGIN PUBLIC KEY
CHECK ok : a PrivateKey encodes as BEGIN PRIVATE KEY (PKCS#8)
CHECK ok : the decoded public key equals the original
CHECK ok : the decoded private key equals the original
--- decode(String) returns whatever the header says it is
-----BEGIN PUBLIC KEY----- -> PublicKey (EC)
-----BEGIN PRIVATE KEY----- -> PrivateKey (EC)
--- encrypted private key
-----BEGIN ENCRYPTED PRIVATE KEY-----
CHECK ok : withEncryption produces ENCRYPTED PRIVATE KEY
decoded with no password: javax.crypto.EncryptedPrivateKeyInfo
CHECK ok : without a password you get the still-encrypted structure back
CHECK ok : with the password you get the original private key
--- PEM(type, byte[]) does NOT Base64-encode: the bytes go in as they are
-----BEGIN ANKURM DEMO-----
hello, pem
-----END ANKURM DEMO-----
CHECK ok : raw bytes appear verbatim between the header and footer
--- give it the Base64 text and it round-trips
-----BEGIN ANKURM DEMO-----
aGVsbG8sIHBlbQ==
-----END ANKURM DEMO-----
CHECK ok : type, Base64 text (content()) and decoded payload (decode()) all survive
exit=0
+7
View File
@@ -0,0 +1,7 @@
$ java --add-modules jdk.incubator.vector VectorDemo (JDK 27)
java.version = 27
preferred species = Species[float, 16, S_512_BIT] (16 float lanes, machine dependent)
scalar dot = 749999.6
vector dot = 749999.6
CHECK ok : vector and scalar dot products agree to within 0.1%
exit=0
+21
View File
@@ -0,0 +1,21 @@
# Who talks to whom: the key-exchange group each pairing actually negotiates (TLS 1.3, loopback)
=== client JDK 26 -> server JDK 26
client offered : "named groups": [x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192]
server selected: x25519
handshake complete: TLSv1.3 TLS_AES_256_GCM_SHA384
=== client JDK 27 -> server JDK 27
client offered : "named groups": [X25519MLKEM768, x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096]
server selected: X25519MLKEM768
handshake complete: TLSv1.3 TLS_AES_256_GCM_SHA384
=== client JDK 27 -> server JDK 26
client offered : "named groups": [X25519MLKEM768, x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096]
server selected: x25519
handshake complete: TLSv1.3 TLS_AES_256_GCM_SHA384
=== client JDK 26 -> server JDK 27
client offered : "named groups": [x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192]
server selected: x25519
handshake complete: TLSv1.3 TLS_AES_256_GCM_SHA384
+19
View File
@@ -0,0 +1,19 @@
# Size of the first TLS record (the ClientHello) each JDK sends
=== JDK 26
java.version = 26.0.2.1
record type = 0x16 (0x16 = handshake)
ClientHello record = 417 bytes (+5 byte record header)
fits one TCP segment = yes (1460-byte MSS on a 1500-byte MTU)
=== JDK 27
java.version = 27
record type = 0x16 (0x16 = handshake)
ClientHello record = 1573 bytes (+5 byte record header)
fits one TCP segment = NO (1460-byte MSS on a 1500-byte MTU)
=== JDK 27 with -Djdk.tls.namedGroups=x25519,secp256r1 (post-quantum group removed)
java.version = 27
record type = 0x16 (0x16 = handshake)
ClientHello record = 408 bytes (+5 byte record header)
fits one TCP segment = yes (1460-byte MSS on a 1500-byte MTU)
+6
View File
@@ -0,0 +1,6 @@
# Opting out on JDK 27: a client that does not offer the hybrid group
=== client JDK 27 with -Djdk.tls.namedGroups=x25519,secp256r1 -> server JDK 27
client offered : "named groups": [x25519, secp256r1]
server selected: x25519
handshake complete: TLSv1.3 TLS_AES_256_GCM_SHA384
+41
View File
@@ -0,0 +1,41 @@
# The process under test: java -Dapi.token=abc123 -Dregion=ap-south-1 ... Idle --password=hunter2 --user=ankur
# with DB_PASSWORD=hunter2 in the environment. Which of those values reach the recording?
=== jdk26-default
jvmArguments = "-Dapi.token=abc123 -Dregion=ap-south-1 -XX:StartFlightRecording:filename=<file>"
javaArguments = "Idle --password=hunter2 --user=ankur"
key = "region" value = "ap-south-1"
key = "api.token" value = "abc123"
key = "DB_PASSWORD" value = "hunter2"
=== jdk27-default
jvmArguments = "[REDACTED] -Dregion=ap-south-1 -XX:StartFlightRecording:filename=<file>"
javaArguments = "Idle [REDACTED] --user=ankur"
key = "region" value = "ap-south-1"
key = "api.token" value = "[REDACTED]"
key = "DB_PASSWORD" value = "[REDACTED]"
=== jdk27-own-list-replaces-defaults
-XX:FlightRecorderOptions:redact-argument=--user*,redact-key=region
jvmArguments = "-Dapi.token=abc123 -Dregion=[REDACTED] -XX:FlightRecorderOptions:redact-argument=[REDACTED],redact-key=region -XX:StartFlightRecording:filename=<file>"
javaArguments = "Idle --password=hunter2 [REDACTED]"
key = "region" value = "[REDACTED]"
key = "api.token" value = "abc123"
key = "DB_PASSWORD" value = "hunter2"
=== jdk27-plus-extends-defaults
-XX:FlightRecorderOptions:redact-argument=+--user*,redact-key=+region
jvmArguments = "[REDACTED] -Dregion=[REDACTED] -XX:FlightRecorderOptions:redact-argument=[REDACTED],redact-key=+region -XX:StartFlightRecording:filename=<file>"
javaArguments = "Idle [REDACTED] [REDACTED]"
key = "region" value = "[REDACTED]"
key = "api.token" value = "[REDACTED]"
key = "DB_PASSWORD" value = "[REDACTED]"
=== jdk27-none-disables-redaction
-XX:FlightRecorderOptions:redact-argument=none,redact-key=none
jvmArguments = "-Dapi.token=abc123 -Dregion=ap-south-1 -XX:FlightRecorderOptions:redact-argument=[REDACTED],redact-key=none -XX:StartFlightRecording:filename=<file>"
javaArguments = "Idle --password=hunter2 --user=ankur"
key = "region" value = "ap-south-1"
key = "api.token" value = "abc123"
key = "DB_PASSWORD" value = "hunter2"
+8
View File
@@ -0,0 +1,8 @@
# jdk.incubator.vector: what changed between 26 and 27 (JEP 529 was the 11th incubation in 26, JEP 537 is the 12th in 27)
## public classes removed
## public classes added
## public class changes
jdk.incubator.vector.VectorOperators
- public abstract class jdk.incubator.vector.VectorOperators {
+ public final class jdk.incubator.vector.VectorOperators {
+190
View File
@@ -0,0 +1,190 @@
# Public API removed/added/changed between JDK 26 and 27 (javap -public of every exported class; internal packages skipped)
## public classes removed
java.security.DEREncodable
java.util.concurrent.StructuredTaskScope$FailedException
java.util.concurrent.StructuredTaskScope$TimeoutException
## public classes added
com.sun.source.tree.VarTypeTree
java.security.BinaryEncodable
java.time.format.DateTimeFormatterPatternProvider
java.util.concurrent.StructuredTaskScope$CancelledByTimeoutException
javax.crypto.CryptoException
## public class changes
com.sun.source.tree.Tree$Kind
+ public static final com.sun.source.tree.Tree$Kind VAR_TYPE;
com.sun.source.tree.TreeVisitor
+ public abstract R visitVarType(com.sun.source.tree.VarTypeTree, P);
com.sun.source.util.DocSourcePositions
- public abstract long getEndPosition(com.sun.source.tree.CompilationUnitTree, com.sun.source.doctree.DocCommentTree, com.sun.source.doctree.DocTree);
- public abstract long getStartPosition(com.sun.source.tree.CompilationUnitTree, com.sun.source.doctree.DocCommentTree, com.sun.source.doctree.DocTree);
+ public abstract long getEndPosition(com.sun.source.doctree.DocCommentTree, com.sun.source.doctree.DocTree);
+ public abstract long getStartPosition(com.sun.source.doctree.DocCommentTree, com.sun.source.doctree.DocTree);
+ public default long getEndPosition(com.sun.source.tree.CompilationUnitTree, com.sun.source.doctree.DocCommentTree, com.sun.source.doctree.DocTree);
+ public default long getStartPosition(com.sun.source.tree.CompilationUnitTree, com.sun.source.doctree.DocCommentTree, com.sun.source.doctree.DocTree);
com.sun.source.util.SimpleTreeVisitor
+ public R visitVarType(com.sun.source.tree.VarTypeTree, P);
com.sun.source.util.SourcePositions
- public abstract long getEndPosition(com.sun.source.tree.CompilationUnitTree, com.sun.source.tree.Tree);
- public abstract long getStartPosition(com.sun.source.tree.CompilationUnitTree, com.sun.source.tree.Tree);
+ public abstract long getEndPosition(com.sun.source.tree.Tree);
+ public abstract long getStartPosition(com.sun.source.tree.Tree);
+ public default long getEndPosition(com.sun.source.tree.CompilationUnitTree, com.sun.source.tree.Tree);
+ public default long getStartPosition(com.sun.source.tree.CompilationUnitTree, com.sun.source.tree.Tree);
com.sun.source.util.TreeScanner
+ public R visitVarType(com.sun.source.tree.VarTypeTree, P);
java.awt.KeyboardFocusManager
- public static java.awt.KeyboardFocusManager getCurrentKeyboardFocusManager();
+ public static synchronized java.awt.KeyboardFocusManager getCurrentKeyboardFocusManager();
java.awt.geom.Arc2D
+ public java.awt.Rectangle getBounds();
java.lang.LazyConstant
- public abstract T orElse(T);
- public abstract boolean isInitialized();
java.lang.Math
+ public static double acosh(double);
+ public static double asinh(double);
+ public static double atanh(double);
java.lang.StrictMath
+ public static double acosh(double);
+ public static double asinh(double);
+ public static double atanh(double);
java.lang.String
+ public int encodedLength(java.nio.charset.Charset);
java.lang.classfile.ClassFile
+ public static final int JAVA_27_VERSION;
java.lang.classfile.instruction.IncrementInstruction
+ public static java.lang.classfile.instruction.IncrementInstruction of(java.lang.classfile.Opcode, int, int);
java.lang.foreign.MemorySegment
+ public abstract java.lang.String getString(long, java.nio.charset.Charset, long);
+ public static long copy(java.lang.String, java.nio.charset.Charset, int, java.lang.foreign.MemorySegment, long, int);
java.lang.foreign.SegmentAllocator
+ public default java.lang.foreign.MemorySegment allocateFrom(java.lang.String, java.nio.charset.Charset, int, int);
java.lang.reflect.ClassFileFormatVersion
+ public static final java.lang.reflect.ClassFileFormatVersion RELEASE_27;
java.lang.reflect.Executable
- public abstract java.lang.String toGenericString();
+ public java.lang.String toGenericString();
java.math.BigDecimal
+ public java.math.BigDecimal rootn(int, java.math.MathContext);
java.security.AsymmetricKey
- public interface java.security.AsymmetricKey extends java.security.Key,java.security.DEREncodable {
+ public interface java.security.AsymmetricKey extends java.security.Key,java.security.BinaryEncodable {
java.security.KeyPair
- public final class java.security.KeyPair implements java.io.Serializable,java.security.DEREncodable {
+ public final class java.security.KeyPair implements java.io.Serializable,java.security.BinaryEncodable {
java.security.KeyStore
+ public final java.time.Instant getCreationInstant(java.lang.String) throws java.security.KeyStoreException;
java.security.KeyStoreSpi
+ public java.time.Instant engineGetCreationInstant(java.lang.String);
java.security.PEM
- public final class java.security.PEM extends java.lang.Record implements java.security.DEREncodable {
+ public final class java.security.PEM implements java.security.BinaryEncodable {
- public final boolean equals(java.lang.Object);
- public final byte[] decode();
- public final int hashCode();
- public final java.lang.String toString();
- public java.lang.String content();
+ public byte[] content();
+ public byte[] decode();
+ public java.lang.String toString();
+ public java.security.PEM(java.lang.String, byte[]);
+ public java.security.PEM(java.lang.String, byte[], byte[]);
java.security.PEMDecoder
- public <S extends java.security.DEREncodable> S decode(java.io.InputStream, java.lang.Class<S>) throws java.io.IOException;
- public <S extends java.security.DEREncodable> S decode(java.lang.String, java.lang.Class<S>);
- public java.security.DEREncodable decode(java.io.InputStream) throws java.io.IOException;
- public java.security.DEREncodable decode(java.lang.String);
- public java.security.PEMDecoder withFactory(java.security.Provider);
+ public <S extends java.security.BinaryEncodable> S decode(java.io.InputStream, java.lang.Class<S>) throws java.io.IOException;
+ public <S extends java.security.BinaryEncodable> S decode(java.lang.String, java.lang.Class<S>);
+ public java.security.BinaryEncodable decode(java.io.InputStream) throws java.io.IOException;
+ public java.security.BinaryEncodable decode(java.lang.String);
+ public java.security.PEMDecoder withFactoriesOf(java.security.Provider);
java.security.PEMEncoder
- public byte[] encode(java.security.DEREncodable);
- public java.lang.String encodeToString(java.security.DEREncodable);
+ public byte[] encode(java.security.BinaryEncodable);
+ public java.lang.String encodeToString(java.security.BinaryEncodable);
java.security.cert.X509CRL
- public abstract class java.security.cert.X509CRL extends java.security.cert.CRL implements java.security.cert.X509Extension,java.security.DEREncodable {
+ public abstract class java.security.cert.X509CRL extends java.security.cert.CRL implements java.security.cert.X509Extension,java.security.BinaryEncodable {
java.security.cert.X509Certificate
- public abstract class java.security.cert.X509Certificate extends java.security.cert.Certificate implements java.security.cert.X509Extension,java.security.DEREncodable {
+ public abstract class java.security.cert.X509Certificate extends java.security.cert.Certificate implements java.security.cert.X509Extension,java.security.BinaryEncodable {
java.security.spec.PKCS8EncodedKeySpec
- public class java.security.spec.PKCS8EncodedKeySpec extends java.security.spec.EncodedKeySpec implements java.security.DEREncodable {
+ public class java.security.spec.PKCS8EncodedKeySpec extends java.security.spec.EncodedKeySpec implements java.security.BinaryEncodable {
java.security.spec.X509EncodedKeySpec
- public class java.security.spec.X509EncodedKeySpec extends java.security.spec.EncodedKeySpec implements java.security.DEREncodable {
+ public class java.security.spec.X509EncodedKeySpec extends java.security.spec.EncodedKeySpec implements java.security.BinaryEncodable {
java.util.EnumSet
+ public java.util.Spliterator<E> spliterator();
java.util.Locale
- public final java.lang.String getDisplayCountry();
- public final java.lang.String getDisplayLanguage();
- public final java.lang.String getDisplayName();
- public final java.lang.String getDisplayVariant();
+ public java.lang.String getDisplayCountry();
+ public java.lang.String getDisplayLanguage();
+ public java.lang.String getDisplayName();
+ public java.lang.String getDisplayVariant();
java.util.Set
+ public static <E> java.util.Set<E> ofLazy(java.util.Set<? extends E>, java.util.function.Predicate<? super E>);
java.util.concurrent.StructuredTaskScope
- public interface java.util.concurrent.StructuredTaskScope<T, R> extends java.lang.AutoCloseable {
+ public interface java.util.concurrent.StructuredTaskScope<T, R, R_X extends java.lang.Throwable> extends java.lang.AutoCloseable {
- public abstract R join() throws java.lang.InterruptedException;
- public static <T, R> java.util.concurrent.StructuredTaskScope<T, R> open(java.util.concurrent.StructuredTaskScope$Joiner<? super T, ? extends R>);
- public static <T, R> java.util.concurrent.StructuredTaskScope<T, R> open(java.util.concurrent.StructuredTaskScope$Joiner<? super T, ? extends R>, java.util.function.UnaryOperator<java.util.concurrent.StructuredTaskScope$Configuration>);
- public static <T> java.util.concurrent.StructuredTaskScope<T, java.lang.Void> open();
+ public abstract R join() throws R_X, java.lang.InterruptedException;
+ public static <T, R, R_X extends java.lang.Throwable> java.util.concurrent.StructuredTaskScope<T, R, R_X> open(java.util.concurrent.StructuredTaskScope$Joiner<? super T, ? extends R, R_X>);
+ public static <T, R, R_X extends java.lang.Throwable> java.util.concurrent.StructuredTaskScope<T, R, R_X> open(java.util.concurrent.StructuredTaskScope$Joiner<? super T, ? extends R, R_X>, java.util.function.UnaryOperator<java.util.concurrent.StructuredTaskScope$Configuration>);
+ public static <T> java.util.concurrent.StructuredTaskScope<T, java.lang.Void, java.util.concurrent.ExecutionException> open();
+ public static <T> java.util.concurrent.StructuredTaskScope<T, java.lang.Void, java.util.concurrent.ExecutionException> open(java.util.function.UnaryOperator<java.util.concurrent.StructuredTaskScope$Configuration>);
java.util.concurrent.StructuredTaskScope$Joiner
- public interface java.util.concurrent.StructuredTaskScope$Joiner<T, R> {
+ public interface java.util.concurrent.StructuredTaskScope$Joiner<T, R, R_X extends java.lang.Throwable> {
- public abstract R result() throws java.lang.Throwable;
- public default void onTimeout();
- public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, T> anySuccessfulOrThrow();
- public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.lang.Void> awaitAll();
- public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.lang.Void> awaitAllSuccessfulOrThrow();
- public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.util.List<T>> allSuccessfulOrThrow();
- public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.util.List<java.util.concurrent.StructuredTaskScope$Subtask<T>>> allUntil(java.util.function.Predicate<java.util.concurrent.StructuredTaskScope$Subtask<T>>);
+ public abstract R result() throws R_X;
+ public abstract R timeout() throws R_X;
+ public static <T, R_X extends java.lang.Throwable> java.util.concurrent.StructuredTaskScope$Joiner<T, T, R_X> anySuccessfulOrThrow(java.util.function.Function<java.lang.Throwable, R_X>);
+ public static <T, R_X extends java.lang.Throwable> java.util.concurrent.StructuredTaskScope$Joiner<T, java.lang.Void, R_X> awaitAllSuccessfulOrThrow(java.util.function.Function<java.lang.Throwable, R_X>);
+ public static <T, R_X extends java.lang.Throwable> java.util.concurrent.StructuredTaskScope$Joiner<T, java.util.List<T>, R_X> allSuccessfulOrThrow(java.util.function.Function<java.lang.Throwable, R_X>);
+ public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, T, java.util.concurrent.ExecutionException> anySuccessfulOrThrow();
+ public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.lang.Void, java.util.concurrent.ExecutionException> awaitAllSuccessfulOrThrow();
+ public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.util.List<T>, java.util.concurrent.ExecutionException> allSuccessfulOrThrow();
+ public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.util.List<java.util.concurrent.StructuredTaskScope$Subtask<T>>, java.lang.RuntimeException> allUntil(java.util.function.Predicate<? super java.util.concurrent.StructuredTaskScope$Subtask<T>>);
javax.crypto.EncryptedPrivateKeyInfo
- public class javax.crypto.EncryptedPrivateKeyInfo implements java.security.DEREncodable {
+ public class javax.crypto.EncryptedPrivateKeyInfo implements java.security.BinaryEncodable {
- public java.security.KeyPair getKeyPair(java.security.Key, java.security.Provider) throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException;
- public java.security.PrivateKey getKey(java.security.Key, java.security.Provider) throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException;
- public static javax.crypto.EncryptedPrivateKeyInfo encrypt(java.security.DEREncodable, char[]);
- public static javax.crypto.EncryptedPrivateKeyInfo encrypt(java.security.DEREncodable, char[], java.lang.String, java.security.spec.AlgorithmParameterSpec, java.security.Provider);
- public static javax.crypto.EncryptedPrivateKeyInfo encrypt(java.security.DEREncodable, java.security.Key, java.lang.String, java.security.spec.AlgorithmParameterSpec, java.security.Provider, java.security.SecureRandom);
+ public java.security.KeyPair getKeyPair(java.security.Key) throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException;
+ public java.security.PrivateKey getKey(java.security.Key) throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException;
+ public static javax.crypto.EncryptedPrivateKeyInfo encrypt(java.security.BinaryEncodable, char[]);
+ public static javax.crypto.EncryptedPrivateKeyInfo encrypt(java.security.BinaryEncodable, char[], java.lang.String, java.security.spec.AlgorithmParameterSpec, java.security.Provider);
+ public static javax.crypto.EncryptedPrivateKeyInfo encrypt(java.security.BinaryEncodable, java.security.Key, java.lang.String, java.security.spec.AlgorithmParameterSpec, java.security.Provider, java.security.SecureRandom);
javax.lang.model.SourceVersion
+ public static final javax.lang.model.SourceVersion RELEASE_27;
javax.swing.JEditorPane$JEditorPaneAccessibleHypertextSupport
+ public void changedUpdate(javax.swing.event.DocumentEvent);
+ public void insertUpdate(javax.swing.event.DocumentEvent);
+ public void removeUpdate(javax.swing.event.DocumentEvent);
jdk.incubator.vector.VectorOperators
- public abstract class jdk.incubator.vector.VectorOperators {
+ public final class jdk.incubator.vector.VectorOperators {
jdk.javadoc.doclet.Taglet
+ public default java.lang.String toString(java.util.List<? extends com.sun.source.doctree.DocTree>, javax.lang.model.element.Element, java.net.URI);
jdk.jfr.events.ContainerMemoryUsageEvent
+ public long hostMemoryUsage;
+197
View File
@@ -0,0 +1,197 @@
# Public API removed/added/changed between JDK 25 and 26
## public classes removed
java.applet.Applet
java.applet.Applet$AccessibleApplet
java.applet.AppletContext
java.applet.AppletStub
java.applet.AudioClip
java.beans.AppletInitializer
java.lang.StableValue
java.security.PEMRecord
javax.management.modelmbean.XMLParseException
javax.swing.JApplet
javax.swing.JApplet$AccessibleJApplet
## public classes added
java.lang.LazyConstant
java.net.http.HttpOption
java.net.http.HttpOption$Http3DiscoveryMode
java.net.http.HttpResponse$PushPromiseHandler$PushId
java.net.http.HttpResponse$PushPromiseHandler$PushId$Http3PushId
java.net.http.StreamLimitException
java.net.http.UnsupportedProtocolVersionException
java.security.PEM
javax.crypto.spec.HPKEParameterSpec
jdk.jfr.events.FinalFieldMutationEvent
jdk.jshell.SourceCodeAnalysis$CompletionContext
jdk.jshell.SourceCodeAnalysis$CompletionState
jdk.jshell.SourceCodeAnalysis$ElementSuggestion
jdk.jshell.SourceCodeAnalysis$ElementSuggestionConvertor
## public class changes
com.sun.net.httpserver.HttpExchange
+ public static final long RSPBODY_CHUNKED;
+ public static final long RSPBODY_EMPTY;
java.awt.Robot
+ public static final int DEFAULT_DELAY;
+ public static final int DEFAULT_STEP_LENGTH;
+ public synchronized void type(char);
+ public synchronized void type(int);
+ public void click();
+ public void click(int);
+ public void glide(int, int);
+ public void glide(int, int, int, int);
+ public void glide(int, int, int, int, int, int);
+ public void waitForIdle(int);
java.beans.Beans
- public static java.lang.Object instantiate(java.lang.ClassLoader, java.lang.String, java.beans.beancontext.BeanContext, java.beans.AppletInitializer) throws java.io.IOException, java.lang.ClassNotFoundException;
java.lang.Character$UnicodeBlock
+ public static final java.lang.Character$UnicodeBlock BERIA_ERFE;
+ public static final java.lang.Character$UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_J;
+ public static final java.lang.Character$UnicodeBlock MISCELLANEOUS_SYMBOLS_SUPPLEMENT;
+ public static final java.lang.Character$UnicodeBlock SHARADA_SUPPLEMENT;
+ public static final java.lang.Character$UnicodeBlock SIDETIC;
+ public static final java.lang.Character$UnicodeBlock TAI_YO;
+ public static final java.lang.Character$UnicodeBlock TANGUT_COMPONENTS_SUPPLEMENT;
+ public static final java.lang.Character$UnicodeBlock TOLONG_SIKI;
java.lang.Character$UnicodeScript
+ public static final java.lang.Character$UnicodeScript BERIA_ERFE;
+ public static final java.lang.Character$UnicodeScript SIDETIC;
+ public static final java.lang.Character$UnicodeScript TAI_YO;
+ public static final java.lang.Character$UnicodeScript TOLONG_SIKI;
java.lang.Process
- public abstract class java.lang.Process {
+ public abstract class java.lang.Process implements java.io.Closeable {
+ public void close() throws java.io.IOException;
java.lang.String
+ public boolean equalsFoldCase(java.lang.String);
+ public int compareToFoldCase(java.lang.String);
+ public static final java.util.Comparator<java.lang.String> UNICODE_CASEFOLD_ORDER;
java.lang.Thread
- public final void stop();
java.lang.classfile.ClassFile
+ public static final int JAVA_26_VERSION;
java.lang.management.MemoryMXBean
+ public default long getTotalGcCpuTime();
java.lang.reflect.ClassFileFormatVersion
+ public static final java.lang.reflect.ClassFileFormatVersion RELEASE_26;
java.math.BigInteger
+ public java.math.BigInteger rootn(int);
+ public java.math.BigInteger[] rootnAndRemainder(int);
java.net.MulticastSocket
- public byte getTTL() throws java.io.IOException;
- public void send(java.net.DatagramPacket, byte) throws java.io.IOException;
- public void setTTL(byte) throws java.io.IOException;
java.net.http.HttpClient$Version
+ public static final java.net.http.HttpClient$Version HTTP_3;
java.net.http.HttpRequest
+ public <T> java.util.Optional<T> getOption(java.net.http.HttpOption<T>);
java.net.http.HttpRequest$BodyPublishers
+ public static java.net.http.HttpRequest$BodyPublisher ofFileChannel(java.nio.channels.FileChannel, long, long) throws java.io.IOException;
java.net.http.HttpRequest$Builder
+ public default <T> java.net.http.HttpRequest$Builder setOption(java.net.http.HttpOption<T>, T);
java.net.http.HttpResponse$PushPromiseHandler
+ public default void applyPushPromise(java.net.http.HttpRequest, java.net.http.HttpRequest, java.net.http.HttpResponse$PushPromiseHandler$PushId, java.util.function.Function<java.net.http.HttpResponse$BodyHandler<T>, java.util.concurrent.CompletableFuture<java.net.http.HttpResponse<T>>>);
+ public default void notifyAdditionalPromise(java.net.http.HttpRequest, java.net.http.HttpResponse$PushPromiseHandler$PushId);
java.nio.ByteOrder
- public final class java.nio.ByteOrder {
+ public final class java.nio.ByteOrder extends java.lang.Enum<java.nio.ByteOrder> {
- public java.lang.String toString();
+ public static java.nio.ByteOrder valueOf(java.lang.String);
+ public static java.nio.ByteOrder[] values();
java.security.SecureRandom
+ public long nextLong();
java.sql.Array
- public interface java.sql.Array {
+ public interface java.sql.Array extends java.lang.AutoCloseable {
+ public default void close() throws java.sql.SQLException;
java.sql.Blob
- public interface java.sql.Blob {
+ public interface java.sql.Blob extends java.lang.AutoCloseable {
+ public default void close() throws java.sql.SQLException;
java.sql.Clob
- public interface java.sql.Clob {
+ public interface java.sql.Clob extends java.lang.AutoCloseable {
+ public default void close() throws java.sql.SQLException;
java.sql.Connection
+ public default boolean isSimpleIdentifier(java.lang.String) throws java.sql.SQLException;
+ public default java.lang.String enquoteIdentifier(java.lang.String, boolean) throws java.sql.SQLException;
+ public default java.lang.String enquoteLiteral(java.lang.String) throws java.sql.SQLException;
+ public default java.lang.String enquoteNCharLiteral(java.lang.String) throws java.sql.SQLException;
java.sql.JDBCType
+ public static final java.sql.JDBCType DECFLOAT;
+ public static final java.sql.JDBCType JSON;
java.sql.SQLXML
- public interface java.sql.SQLXML {
+ public interface java.sql.SQLXML extends java.lang.AutoCloseable {
+ public default void close() throws java.sql.SQLException;
java.sql.Types
+ public static final int DECFLOAT;
+ public static final int JSON;
java.time.Duration
+ public static final java.time.Duration MAX;
+ public static final java.time.Duration MIN;
java.time.Instant
+ public java.time.Instant plusSaturating(java.time.Duration);
java.util.Comparator
+ public default <U extends T> U max(U, U);
+ public default <U extends T> U min(U, U);
java.util.List
+ public static <E> java.util.List<E> ofLazy(int, java.util.function.IntFunction<? extends E>);
java.util.Locale$IsoCountryCode
- public abstract class java.util.Locale$IsoCountryCode extends java.lang.Enum<java.util.Locale$IsoCountryCode> {
+ public final class java.util.Locale$IsoCountryCode extends java.lang.Enum<java.util.Locale$IsoCountryCode> {
java.util.Map
+ public static <K, V> java.util.Map<K, V> ofLazy(java.util.Set<? extends K>, java.util.function.Function<? super K, ? extends V>);
java.util.UUID
+ public static java.util.UUID ofEpochMillis(long);
java.util.concurrent.StructuredTaskScope
- public static <T, R> java.util.concurrent.StructuredTaskScope<T, R> open(java.util.concurrent.StructuredTaskScope$Joiner<? super T, ? extends R>, java.util.function.Function<java.util.concurrent.StructuredTaskScope$Configuration, java.util.concurrent.StructuredTaskScope$Configuration>);
+ public static <T, R> java.util.concurrent.StructuredTaskScope<T, R> open(java.util.concurrent.StructuredTaskScope$Joiner<? super T, ? extends R>, java.util.function.UnaryOperator<java.util.concurrent.StructuredTaskScope$Configuration>);
java.util.concurrent.StructuredTaskScope$Joiner
- public default boolean onComplete(java.util.concurrent.StructuredTaskScope$Subtask<? extends T>);
- public default boolean onFork(java.util.concurrent.StructuredTaskScope$Subtask<? extends T>);
- public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, T> anySuccessfulResultOrThrow();
- public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.util.stream.Stream<java.util.concurrent.StructuredTaskScope$Subtask<T>>> allSuccessfulOrThrow();
- public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.util.stream.Stream<java.util.concurrent.StructuredTaskScope$Subtask<T>>> allUntil(java.util.function.Predicate<java.util.concurrent.StructuredTaskScope$Subtask<? extends T>>);
+ public default boolean onComplete(java.util.concurrent.StructuredTaskScope$Subtask<T>);
+ public default boolean onFork(java.util.concurrent.StructuredTaskScope$Subtask<T>);
+ public default void onTimeout();
+ public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, T> anySuccessfulOrThrow();
+ public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.util.List<T>> allSuccessfulOrThrow();
+ public static <T> java.util.concurrent.StructuredTaskScope$Joiner<T, java.util.List<java.util.concurrent.StructuredTaskScope$Subtask<T>>> allUntil(java.util.function.Predicate<java.util.concurrent.StructuredTaskScope$Subtask<T>>);
java.util.concurrent.ThreadLocalRandom
+ public double nextGaussian();
javax.crypto.EncryptedPrivateKeyInfo
- public java.security.PrivateKey getKey(char[]) throws java.security.GeneralSecurityException;
- public java.security.PrivateKey getKey(java.security.Key, java.security.Provider) throws java.security.GeneralSecurityException;
- public static javax.crypto.EncryptedPrivateKeyInfo encryptKey(java.security.PrivateKey, char[]);
- public static javax.crypto.EncryptedPrivateKeyInfo encryptKey(java.security.PrivateKey, char[], java.lang.String, java.security.spec.AlgorithmParameterSpec, java.security.Provider);
- public static javax.crypto.EncryptedPrivateKeyInfo encryptKey(java.security.PrivateKey, java.security.Key, java.lang.String, java.security.spec.AlgorithmParameterSpec, java.security.Provider, java.security.SecureRandom);
+ public java.security.KeyPair getKeyPair(char[]) throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException;
+ public java.security.KeyPair getKeyPair(java.security.Key, java.security.Provider) throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException;
+ public java.security.PrivateKey getKey(char[]) throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException;
+ public java.security.PrivateKey getKey(java.security.Key, java.security.Provider) throws java.security.NoSuchAlgorithmException, java.security.InvalidKeyException;
+ public static javax.crypto.EncryptedPrivateKeyInfo encrypt(java.security.DEREncodable, char[]);
+ public static javax.crypto.EncryptedPrivateKeyInfo encrypt(java.security.DEREncodable, char[], java.lang.String, java.security.spec.AlgorithmParameterSpec, java.security.Provider);
+ public static javax.crypto.EncryptedPrivateKeyInfo encrypt(java.security.DEREncodable, java.security.Key, java.lang.String, java.security.spec.AlgorithmParameterSpec, java.security.Provider, java.security.SecureRandom);
javax.imageio.spi.ServiceRegistry
- public void finalize() throws java.lang.Throwable;
javax.lang.model.SourceVersion
+ public static final javax.lang.model.SourceVersion RELEASE_26;
javax.management.modelmbean.DescriptorSupport
- public javax.management.modelmbean.DescriptorSupport(java.lang.String) throws javax.management.MBeanException, javax.management.RuntimeOperationsException, javax.management.modelmbean.XMLParseException;
- public synchronized java.lang.String toXMLString();
javax.swing.RepaintManager
- public void addDirtyRegion(java.applet.Applet, int, int, int, int);
jdk.incubator.vector.VectorOperators
- public static final jdk.incubator.vector.VectorOperators$Binary SUADD;
+ public static final jdk.incubator.vector.VectorOperators$Associative SUADD;
jdk.jpackage.main.Main
- public int execute(java.io.PrintWriter, java.io.PrintWriter, java.lang.String...);
- public static final java.util.ResourceBundle I18N;
- public static void main(java.lang.String...) throws java.lang.Exception;
+ public static void main(java.lang.String...);
jdk.jshell.SourceCodeAnalysis
+ public abstract <S> java.util.List<S> completionSuggestions(java.lang.String, int, jdk.jshell.SourceCodeAnalysis$ElementSuggestionConvertor<S>);
jdk.jshell.SourceCodeAnalysis$Documentation
+ public default int activeParameterIndex();
+54
View File
@@ -0,0 +1,54 @@
# JVM options that JDK 27 removed or renamed. Each runs 'java <option> -version' on 26 and on 27.
=== -noverify
JDK 26:
OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.
OpenJDK 64-Bit Server VM Corretto-26.0.2.11.1 (build 26.0.2.1+11-FR, mixed mode, sharing)
JDK 27:
Unrecognized option: -noverify
Error: Could not create the Java Virtual Machine.
=== -Xverify:none
JDK 26:
OpenJDK 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.
OpenJDK 64-Bit Server VM Corretto-26.0.2.11.1 (build 26.0.2.1+11-FR, mixed mode, sharing)
JDK 27:
Unrecognized verification option: -Xverify:none
Error: Could not create the Java Virtual Machine.
=== -noclassgc
JDK 26:
Warning: -noclassgc option is deprecated and may be removed in a future release.
OpenJDK 64-Bit Server VM Corretto-26.0.2.11.1 (build 26.0.2.1+11-FR, mixed mode, sharing)
JDK 27:
Unrecognized option: -noclassgc
Error: Could not create the Java Virtual Machine.
=== -XX:+UseCompressedClassPointers
JDK 26:
OpenJDK 64-Bit Server VM warning: Option UseCompressedClassPointers was deprecated in version 25.0 and will likely be removed in a future release.
OpenJDK 64-Bit Server VM Corretto-26.0.2.11.1 (build 26.0.2.1+11-FR, mixed mode, sharing)
JDK 27:
OpenJDK 64-Bit Server VM warning: Ignoring option UseCompressedClassPointers; support was removed in 27.0
OpenJDK 64-Bit Server VM Corretto-27.0.0.35.1 (build 27+35-FR, mixed mode, sharing)
=== -XX:InitiatingHeapOccupancyPercent=40
JDK 26:
OpenJDK 64-Bit Server VM Corretto-26.0.2.11.1 (build 26.0.2.1+11-FR, mixed mode, sharing)
JDK 27:
OpenJDK 64-Bit Server VM warning: Option InitiatingHeapOccupancyPercent was deprecated in version 27.0 and will likely be removed in a future release. Use option G1IHOP instead.
OpenJDK 64-Bit Server VM Corretto-27.0.0.35.1 (build 27+35-FR, mixed mode, sharing)
=== -XX:G1IHOP=40
JDK 26:
Unrecognized VM option 'G1IHOP=40'
Error: Could not create the Java Virtual Machine.
JDK 27:
OpenJDK 64-Bit Server VM Corretto-27.0.0.35.1 (build 27+35-FR, mixed mode, sharing)
=== -XX:+UnlockExperimentalVMOptions -XX:+UseGraalJIT (JVMCI and the Graal JIT hook were removed in 27)
JDK 26:
Cannot use JVMCI compiler: JVMCI compiler 'graal' specified by jvmci.Compiler not found
JDK 27:
Unrecognized VM option 'UseGraalJIT'
Error: Could not create the Java Virtual Machine.
+21
View File
@@ -0,0 +1,21 @@
# Behaviour changes that need no new API to observe
=== a script that pins the VFORK process launch mechanism
JDK 26: java -Djdk.lang.Process.launchMechanism=VFORK SpawnProbe
VFORK MODE DEPRECATED
The VFORK launch mechanism has been deprecated for being dangerous.
It will be removed in a future java version. Either remove the
JDK 27: java -Djdk.lang.Process.launchMechanism=VFORK SpawnProbe
The VFORK launch mechanism has been removed. Switching to FORK instead.
Please remove the jdk.lang.Process.launchMechanism property (preferred)
or use FORK mode instead (-Djdk.lang.Process.launchMechanism=FORK).
=== the legacy ISO 639 language codes property (iw, ji, in)
JDK 26: java -Djava.locale.useOldISOCodes=true Compat
java.version = 26.0.2.1
WARNING: The use of the system property "java.locale.useOldISOCodes" is deprecated. It will be removed in a future release of the JDK.
Locale("iw").language = iw (Hebrew; old code 'iw', current code 'he')
JDK 27: java -Djava.locale.useOldISOCodes=true Compat
java.version = 27
WARNING: The system property "java.locale.useOldISOCodes" is no longer supported. Any specified value will be ignored.
Locale("iw").language = he (Hebrew; old code 'iw', current code 'he')
+12
View File
@@ -0,0 +1,12 @@
# New public API in 27, each exercised once (found by diffing javap dumps, see 51-api-diff-26-to-27.txt)
Math.acosh(2) = 1.316958
Math.asinh(1) = 0.881374
Math.atanh(.5) = 0.549306
CHECK ok : acosh inverts cosh
"naïve café".length() = 10, encodedLength(UTF_8) = 12
CHECK ok : encodedLength matches getBytes().length without allocating the array
BigDecimal(27).rootn(3) = 3
CHECK ok : rootn(3) of 27 is 3
KeyStore.getCreationInstant("aes-key") returns an Instant: true
CHECK ok : KeyStore.getCreationInstant returns the entry's creation time as a java.time.Instant
CHECK ok : Set.ofLazy(Set, Predicate) exists (preview API)
+45
View File
@@ -0,0 +1,45 @@
# JEP 500: reflection writing to a final field, same class file behaviour on 25 and on 26
=== JDK 25 (class compiled --release 25)
field reads : 9090
port() returns : 8080
=== JDK 26, defaults
WARNING: Final field port in class FinalFieldMutation$Config has been mutated reflectively by class FinalFieldMutation in unnamed module @<hash> (file:<repo>/build/recap26/26/)
WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled
field reads : 9090
port() returns : 8080
=== JDK 26, --enable-final-field-mutation=ALL-UNNAMED (the opt-in the warning asks for)
field reads : 9090
port() returns : 8080
=== JDK 26, --illegal-final-field-mutation=deny (what a future default will look like)
setInt refused : class FinalFieldMutation (in unnamed module @<hash>) cannot set final field FinalFieldMutation$Config.port (in unnamed module @<hash>), unnamed module @<hash> is not allowed to mutate final fields
=== JDK 26, --illegal-final-field-mutation=debug (warning plus the offending stack trace)
Final field port in class FinalFieldMutation$Config has been mutated reflectively by class FinalFieldMutation in unnamed module @<hash> (file:<repo>/build/recap26/26/)
at java.base/java.lang.reflect.Field.postSetFinal(Field.java:1534)
at java.base/java.lang.reflect.Field.setFinal(Field.java:1449)
at java.base/java.lang.reflect.Field.setInt(Field.java:1134)
at FinalFieldMutation.main(FinalFieldMutation.java:22)
field reads : 9090
port() returns : 8080
=== JDK 26, JFR default settings, [no flag]: jfr print --events jdk.FinalFieldMutation
jdk.FinalFieldMutation
declaringClass = FinalFieldMutation$Config (classLoader = app)
fieldName = "port"
FinalFieldMutation.main(String[]) line: 22
events recorded: 1
=== JDK 26, JFR default settings, [--enable-final-field-mutation=ALL-UNNAMED]: jfr print --events jdk.FinalFieldMutation
jdk.FinalFieldMutation
declaringClass = FinalFieldMutation$Config (classLoader = app)
fieldName = "port"
FinalFieldMutation.main(String[]) line: 22
events recorded: 1
=== JDK 26, JFR default settings, [--illegal-final-field-mutation=deny]: jfr print --events jdk.FinalFieldMutation
events recorded: 0
+20
View File
@@ -0,0 +1,20 @@
# JEP 516: can the cache's archived heap objects be used? One row per (JDK, GC the cache was built under, GC at run time).
# 'heap objects' = the log says the archived module graph was used; 'unusable' = the JVM refused the whole cache.
JDK 25 cache built under G1 run under Serial -> heap objects used (full module graph: enabled)
JDK 25 cache built under G1 run under G1 -> heap objects used (full module graph: enabled)
JDK 25 cache built under G1 run under Z -> unusable: saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime
JDK 25 cache built under Z run under Serial -> unusable: saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime
JDK 25 cache built under Z run under G1 -> unusable: saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime
JDK 25 cache built under Z run under Z -> cache used, heap objects NOT used (full module graph: disabled)
JDK 26 cache built under G1 run under Serial -> heap objects used (full module graph: enabled)
JDK 26 cache built under G1 run under G1 -> heap objects used (full module graph: enabled)
JDK 26 cache built under G1 run under Z -> unusable: saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime
JDK 26 cache built under Z run under Serial -> unusable: saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime
JDK 26 cache built under Z run under G1 -> unusable: saved state of UseCompressedOops and UseCompressedClassPointers is different from runtime
JDK 26 cache built under Z run under Z -> heap objects used (full module graph: enabled)
JDK 27 cache built under G1 run under Serial -> heap objects used (full module graph: enabled)
JDK 27 cache built under G1 run under G1 -> heap objects used (full module graph: enabled)
JDK 27 cache built under G1 run under Z -> unusable: saved state of UseCompressedOops (1) is different from runtime (0)
JDK 27 cache built under Z run under Serial -> unusable: saved state of UseCompressedOops (0) is different from runtime (1)
JDK 27 cache built under Z run under G1 -> unusable: saved state of UseCompressedOops (0) is different from runtime (1)
JDK 27 cache built under Z run under Z -> heap objects used (full module graph: enabled)
+7
View File
@@ -0,0 +1,7 @@
# Wall-clock ms for 'java -cp app.jar AotApp' (median of 9), no AOT cache vs cache built under the same GC. INDICATIVE: shared 2-CPU sandbox.
JDK 25 G1 no cache: 57 ms with cache: 37 ms
JDK 25 Z no cache: 83 ms with cache: 81 ms
JDK 26 G1 no cache: 59 ms with cache: 35 ms
JDK 26 Z no cache: 62 ms with cache: 40 ms
JDK 27 G1 no cache: 61 ms with cache: 36 ms
JDK 27 Z no cache: 63 ms with cache: 38 ms
+34
View File
@@ -0,0 +1,34 @@
# Source that compiled on 25 and does not on 26
=== recap26/src/broken/AppletGone.java
javac (JDK 25, --release 25): exit 0
<repo>/recap26/src/broken/AppletGone.java:5: warning: [removal] Applet in java.applet has been deprecated and marked for removal
public class AppletGone extends Applet {
^
1 warning
javac (JDK 26, --release 26): exit 1
<repo>/recap26/src/broken/AppletGone.java:3: error: package java.applet does not exist
import java.applet.Applet;
^
<repo>/recap26/src/broken/AppletGone.java:5: error: cannot find symbol
public class AppletGone extends Applet {
^
symbol: class Applet
<repo>/recap26/src/broken/AppletGone.java:6: error: init() in AppletGone does not override or implement a method from a supertype
@Override public void init() { System.out.println("applet init"); }
^
3 errors
=== recap26/src/broken/ThreadStopGone.java
javac (JDK 25, --release 25): exit 0
<repo>/recap26/src/broken/ThreadStopGone.java:5: warning: [removal] stop() in Thread has been deprecated and marked for removal
t.stop();
^
1 warning
javac (JDK 26, --release 26): exit 1
<repo>/recap26/src/broken/ThreadStopGone.java:5: error: cannot find symbol
t.stop();
^
symbol: method stop()
location: variable t of type Thread
1 error
+12
View File
@@ -0,0 +1,12 @@
CHECK ok : "STRASSE".equalsFoldCase("stra" + sharp s + "e") is true (full case folding)
CHECK ok : equalsIgnoreCase says false for the same pair (simple folding only)
CHECK ok : "apple".compareToFoldCase("BANANA") < 0
CHECK ok : sorted with String.UNICODE_CASEFOLD_ORDER: [A, b, B, c]
CHECK ok : UUID.ofEpochMillis(..).version() == 7
CHECK ok : the first 48 bits are the timestamp: 018bcfe5-6800-...
CHECK ok : try-with-resources on Process (implements Closeable in 26)
CHECK ok : MemoryMXBean.getTotalGcCpuTime() exists and is non-negative
CHECK ok : ByteOrder is an enum: [LITTLE_ENDIAN, BIG_ENDIAN]
CHECK ok : HttpClient.Version.HTTP_3 exists and is accepted by the builder
CHECK ok : Character.UnicodeBlock.SIDETIC exists (Unicode 17)
exit code: 0
+26
View File
@@ -0,0 +1,26 @@
# JEP 517: a client that prefers HTTP/3 against a server that only speaks HTTP/1.1 over TLS (loopback, JDK's own HttpsServer)
# There is no live HTTP/3 server in this transcript: the sandbox has no UDP egress. The API surface is javap output below.
client asked for : HTTP_3
server answered : HTTP_1_1 200 hello
HTTP_3_URI_ONLY : java.net.http.HttpConnectTimeoutException: quic handshake timeout
# javap --module java.net.http -public java.net.http.HttpOption 'java.net.http.HttpOption$Http3DiscoveryMode'
Compiled from "HttpOption.java"
public interface java.net.http.HttpOption<T> {
public static final java.net.http.HttpOption<java.net.http.HttpOption$Http3DiscoveryMode> H3_DISCOVERY;
public abstract java.lang.String name();
public abstract java.lang.Class<T> type();
}
Compiled from "HttpOption.java"
public final class java.net.http.HttpOption$Http3DiscoveryMode extends java.lang.Enum<java.net.http.HttpOption$Http3DiscoveryMode> {
public static final java.net.http.HttpOption$Http3DiscoveryMode ANY;
public static final java.net.http.HttpOption$Http3DiscoveryMode ALT_SVC;
public static final java.net.http.HttpOption$Http3DiscoveryMode HTTP_3_URI_ONLY;
public static java.net.http.HttpOption$Http3DiscoveryMode[] values();
public static java.net.http.HttpOption$Http3DiscoveryMode valueOf(java.lang.String);
}
# javap ... HttpClient$Version
public static final java.net.http.HttpClient$Version HTTP_1_1;
public static final java.net.http.HttpClient$Version HTTP_2;
public static final java.net.http.HttpClient$Version HTTP_3;
+69
View File
@@ -0,0 +1,69 @@
# One class file (javac 21 --release 21), four JVMs
$ java -cp hub HubExamples (JDK 21)
java.version = 21.0.12.1
CHECK ok : record accessor: c.email()
CHECK ok : record toString: Customer[id=1, [email protected], country=IN]
CHECK ok : compact constructor rejects an email without @: Invalid email
CHECK ok : area(Square(3)) == 9.0
CHECK ok : area(Triangle(4, 5)) == 10.0
CHECK ok : guard pattern: Circle(101) is a large circle
CHECK ok : guard pattern: Circle(1) is a small circle
CHECK ok : all 10,000 virtual-thread tasks completed before close() returned
CHECK ok : and it took seconds, not the 1,000 s a serial loop needs (exact time is machine dependent, not printed)
CHECK ok : Thread.ofVirtual().name(..).start(..) gives a virtual thread
CHECK ok : List.getFirst() / getLast()
CHECK ok : List.reversed(): [chris, brian, alex]
CHECK ok : reversed() is a view, not a copy: it sees the element added afterwards
CHECK ok : LinkedHashSet.getFirst() == 3, getLast() == 5
$ java -cp hub HubExamples (JDK 25)
java.version = 25.0.4.1
CHECK ok : record accessor: c.email()
CHECK ok : record toString: Customer[id=1, [email protected], country=IN]
CHECK ok : compact constructor rejects an email without @: Invalid email
CHECK ok : area(Square(3)) == 9.0
CHECK ok : area(Triangle(4, 5)) == 10.0
CHECK ok : guard pattern: Circle(101) is a large circle
CHECK ok : guard pattern: Circle(1) is a small circle
CHECK ok : all 10,000 virtual-thread tasks completed before close() returned
CHECK ok : and it took seconds, not the 1,000 s a serial loop needs (exact time is machine dependent, not printed)
CHECK ok : Thread.ofVirtual().name(..).start(..) gives a virtual thread
CHECK ok : List.getFirst() / getLast()
CHECK ok : List.reversed(): [chris, brian, alex]
CHECK ok : reversed() is a view, not a copy: it sees the element added afterwards
CHECK ok : LinkedHashSet.getFirst() == 3, getLast() == 5
$ java -cp hub HubExamples (JDK 26)
java.version = 26.0.2.1
CHECK ok : record accessor: c.email()
CHECK ok : record toString: Customer[id=1, [email protected], country=IN]
CHECK ok : compact constructor rejects an email without @: Invalid email
CHECK ok : area(Square(3)) == 9.0
CHECK ok : area(Triangle(4, 5)) == 10.0
CHECK ok : guard pattern: Circle(101) is a large circle
CHECK ok : guard pattern: Circle(1) is a small circle
CHECK ok : all 10,000 virtual-thread tasks completed before close() returned
CHECK ok : and it took seconds, not the 1,000 s a serial loop needs (exact time is machine dependent, not printed)
CHECK ok : Thread.ofVirtual().name(..).start(..) gives a virtual thread
CHECK ok : List.getFirst() / getLast()
CHECK ok : List.reversed(): [chris, brian, alex]
CHECK ok : reversed() is a view, not a copy: it sees the element added afterwards
CHECK ok : LinkedHashSet.getFirst() == 3, getLast() == 5
$ java -cp hub HubExamples (JDK 27)
java.version = 27
CHECK ok : record accessor: c.email()
CHECK ok : record toString: Customer[id=1, [email protected], country=IN]
CHECK ok : compact constructor rejects an email without @: Invalid email
CHECK ok : area(Square(3)) == 9.0
CHECK ok : area(Triangle(4, 5)) == 10.0
CHECK ok : guard pattern: Circle(101) is a large circle
CHECK ok : guard pattern: Circle(1) is a small circle
CHECK ok : all 10,000 virtual-thread tasks completed before close() returned
CHECK ok : and it took seconds, not the 1,000 s a serial loop needs (exact time is machine dependent, not printed)
CHECK ok : Thread.ofVirtual().name(..).start(..) gives a virtual thread
CHECK ok : List.getFirst() / getLast()
CHECK ok : List.reversed(): [chris, brian, alex]
CHECK ok : reversed() is a view, not a copy: it sees the element added afterwards
CHECK ok : LinkedHashSet.getFirst() == 3, getLast() == 5
+41
View File
@@ -0,0 +1,41 @@
# ScopedValue (JEP 506). Preview API in 21, final in 25.
$ javac ScopedValueDemo.java (JDK 21, no flag)
<repo>/lanes/src/ScopedValueDemo.java:9: error: ScopedValue is a preview API and is disabled by default.
static final ScopedValue<String> CURRENT_USER = ScopedValue.newInstance();
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/ScopedValueDemo.java:9: error: ScopedValue is a preview API and is disabled by default.
static final ScopedValue<String> CURRENT_USER = ScopedValue.newInstance();
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/ScopedValueDemo.java:13: error: ScopedValue is a preview API and is disabled by default.
ScopedValue.where(CURRENT_USER, "ankur").run(() -> {
^
(use --enable-preview to enable preview APIs)
3 errors
exit=1
$ javac ScopedValueDemo.java (JDK 25, no flag)
exit=0
$ java ScopedValueDemo (JDK 25)
java.version = 25.0.4.1
CHECK ok : inside the scope: CURRENT_USER.get() == ankur
CHECK ok : a method that was never passed the value still sees it
CHECK ok : outside the scope: CURRENT_USER.get() throws NoSuchElementException
$ javac ScopedValueDemo.java (JDK 26, no flag)
exit=0
$ java ScopedValueDemo (JDK 26)
java.version = 26.0.2.1
CHECK ok : inside the scope: CURRENT_USER.get() == ankur
CHECK ok : a method that was never passed the value still sees it
CHECK ok : outside the scope: CURRENT_USER.get() throws NoSuchElementException
$ javac ScopedValueDemo.java (JDK 27, no flag)
exit=0
$ java ScopedValueDemo (JDK 27)
java.version = 27
CHECK ok : inside the scope: CURRENT_USER.get() == ankur
CHECK ok : a method that was never passed the value still sees it
CHECK ok : outside the scope: CURRENT_USER.get() throws NoSuchElementException
+117
View File
@@ -0,0 +1,117 @@
# StructuredTaskScope (JEP 505 in 25). Fifth preview in 25, sixth in 26, seventh in 27.
$ javac StructuredHub.java (JDK 25, no flag)
<repo>/lanes/src/StructuredHub.java:1: error: StructuredTaskScope is a preview API and is disabled by default.
import java.util.concurrent.StructuredTaskScope;
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:15: error: StructuredTaskScope is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open()) {
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:24: error: StructuredTaskScope is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:24: error: Joiner is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:24: error: StructuredTaskScope is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
(use --enable-preview to enable preview APIs)
5 errors
exit=1
$ javac --enable-preview --release 25 StructuredHub.java (JDK 25)
exit=0
$ java --enable-preview StructuredHub (JDK 25)
java.version = 25.0.4.1
CHECK ok : fan-out: both results joined: Dashboard[user=user-7, order=order-for-7]
CHECK ok : anySuccessfulResultOrThrow(): the first result wins and the slow subtask is cancelled
$ javac StructuredHub.java (JDK 26, no flag)
<repo>/lanes/src/StructuredHub.java:1: error: StructuredTaskScope is a preview API and is disabled by default.
import java.util.concurrent.StructuredTaskScope;
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:15: error: StructuredTaskScope is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open()) {
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:24: error: StructuredTaskScope is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:24: error: Joiner is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:24: error: cannot find symbol
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
symbol: method <String>anySuccessfulResultOrThrow()
location: interface Joiner
<repo>/lanes/src/StructuredHub.java:24: error: StructuredTaskScope is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
(use --enable-preview to enable preview APIs)
6 errors
exit=1
$ javac --enable-preview --release 26 StructuredHub.java (JDK 26)
<repo>/lanes/src/StructuredHub.java:24: error: cannot find symbol
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
symbol: method <String>anySuccessfulResultOrThrow()
location: interface Joiner
1 error
exit=1
$ javac StructuredHub.java (JDK 27, no flag)
<repo>/lanes/src/StructuredHub.java:1: error: StructuredTaskScope is a preview API and is disabled by default.
import java.util.concurrent.StructuredTaskScope;
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:15: error: StructuredTaskScope is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open()) {
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:24: error: StructuredTaskScope is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:24: error: Joiner is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
(use --enable-preview to enable preview APIs)
<repo>/lanes/src/StructuredHub.java:24: error: cannot find symbol
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
symbol: method <String>anySuccessfulResultOrThrow()
location: interface Joiner
<repo>/lanes/src/StructuredHub.java:24: error: StructuredTaskScope is a preview API and is disabled by default.
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
(use --enable-preview to enable preview APIs)
6 errors
exit=1
$ javac --enable-preview --release 27 StructuredHub.java (JDK 27)
<repo>/lanes/src/StructuredHub.java:24: error: cannot find symbol
try (var scope = StructuredTaskScope.open(StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
^
symbol: method <String>anySuccessfulResultOrThrow()
location: interface Joiner
1 error
exit=1
$ javac --enable-preview --release 26 StructuredHubNew.java (JDK 26)
exit=0
$ java --enable-preview StructuredHubNew (JDK 26)
java.version = 26.0.2.1
CHECK ok : anySuccessfulOrThrow(): the first result wins
$ javac --enable-preview --release 27 StructuredHubNew.java (JDK 27)
exit=0
$ java --enable-preview StructuredHubNew (JDK 27)
java.version = 27
CHECK ok : anySuccessfulOrThrow(): the first result wins
+29
View File
@@ -0,0 +1,29 @@
# Is finalization removed by default in 25? (JEP 421 deprecated it for removal in 18)
$ java FinalizerDefault (JDK 21)
java.version = 21.0.12.1
finalizer ran: true
$ java --finalization=disabled FinalizerDefault (JDK 21)
java.version = 21.0.12.1
finalizer ran: false
$ java FinalizerDefault (JDK 25)
java.version = 25.0.4.1
finalizer ran: true
$ java --finalization=disabled FinalizerDefault (JDK 25)
java.version = 25.0.4.1
finalizer ran: false
$ java FinalizerDefault (JDK 26)
java.version = 26.0.2.1
finalizer ran: true
$ java --finalization=disabled FinalizerDefault (JDK 26)
java.version = 26.0.2.1
finalizer ran: false
$ java FinalizerDefault (JDK 27)
java.version = 27
finalizer ran: true
$ java --finalization=disabled FinalizerDefault (JDK 27)
java.version = 27
finalizer ran: false
+9
View File
@@ -0,0 +1,9 @@
# System.setSecurityManager(new SecurityManager())
$ java SecurityManagerGone (JDK 21)
java.version = 21.0.12.1
setSecurityManager: UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release
$ java SecurityManagerGone (JDK 25)
java.version = 25.0.4.1
setSecurityManager: UnsupportedOperationException: Setting a Security Manager is not supported
+68
View File
@@ -0,0 +1,68 @@
# Thread.stop(), suspend(), resume(): when did they actually go?
$ java ThreadStopRuntime (JDK 21)
java.version = 21.0.12.1
Thread.stop(): UnsupportedOperationException
$ java ThreadStopRuntime (JDK 25)
java.version = 25.0.4.1
Thread.stop(): UnsupportedOperationException
$ javac ThreadSuspendGone.java (JDK 21)
exit=0
$ javac ThreadSuspendGone.java (JDK 25)
<repo>/lanes/src/ThreadSuspendGone.java:8: error: cannot find symbol
t.suspend();
^
symbol: method suspend()
location: variable t of type Thread
<repo>/lanes/src/ThreadSuspendGone.java:9: error: cannot find symbol
t.resume();
^
symbol: method resume()
location: variable t of type Thread
2 errors
exit=1
$ javac ThreadSuspendGone.java (JDK 26)
<repo>/lanes/src/ThreadSuspendGone.java:8: error: cannot find symbol
t.suspend();
^
symbol: method suspend()
location: variable t of type Thread
<repo>/lanes/src/ThreadSuspendGone.java:9: error: cannot find symbol
t.resume();
^
symbol: method resume()
location: variable t of type Thread
2 errors
exit=1
$ javac ThreadSuspendGone.java (JDK 23, Corretto image)
/src/ThreadSuspendGone.java:8: error: cannot find symbol
t.suspend();
^
symbol: method suspend()
location: variable t of type Thread
/src/ThreadSuspendGone.java:9: error: cannot find symbol
t.resume();
^
symbol: method resume()
location: variable t of type Thread
2 errors
exit=1
$ javac ThreadSuspendGone.java (JDK 24, Corretto image)
/src/ThreadSuspendGone.java:8: error: cannot find symbol
t.suspend();
^
symbol: method suspend()
location: variable t of type Thread
/src/ThreadSuspendGone.java:9: error: cannot find symbol
t.resume();
^
symbol: method resume()
location: variable t of type Thread
2 errors
exit=1
+49
View File
@@ -0,0 +1,49 @@
# sun.misc.Unsafe memory access (JEP 498) and JNI (JEP 472): what does the JVM say?
$ java UnsafeWarning (JDK 21)
java.version = 21.0.12.1
read back: 42
$ java UnsafeWarning (JDK 25)
java.version = 25.0.4.1
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::allocateMemory has been called by UnsafeWarning (file:<repo>/build/lanes/us25/)
WARNING: Please consider reporting this to the maintainers of class UnsafeWarning
WARNING: sun.misc.Unsafe::allocateMemory will be removed in a future release
read back: 42
$ java UnsafeWarning (JDK 24, Corretto image)
java.version = 24.0.2
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::allocateMemory has been called by UnsafeWarning (file:/b/us21/)
WARNING: Please consider reporting this to the maintainers of class UnsafeWarning
WARNING: sun.misc.Unsafe::allocateMemory will be removed in a future release
read back: 42
$ java JniWarning (JDK 21)
java.version = 21.0.12.1
System.loadLibrary("net") returned
$ java --enable-native-access=ALL-UNNAMED JniWarning (JDK 21)
java.version = 21.0.12.1
System.loadLibrary("net") returned
$ java JniWarning (JDK 25)
java.version = 25.0.4.1
WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::loadLibrary has been called by JniWarning in an unnamed module (file:<repo>/build/lanes/jni25/)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
System.loadLibrary("net") returned
$ java --enable-native-access=ALL-UNNAMED JniWarning (JDK 25)
java.version = 25.0.4.1
System.loadLibrary("net") returned
$ java JniWarning (JDK 24, Corretto image)
java.version = 24.0.2
System.loadLibrary("net") returned
WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::loadLibrary has been called by JniWarning in an unnamed module (file:/b/jni21/)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
+25
View File
@@ -0,0 +1,25 @@
# 4 virtual threads on ONE carrier, each sleeping 200 ms inside synchronized (JEP 491, Java 24)
$ java -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1 PinningDemo (JDK 21)
java.version = 21.0.12.1
4 virtual threads, 1 carrier, sleep(200) inside synchronized: SERIALISED (pinned): about 4 x 200 ms
$ java -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1 PinningDemo (JDK 25)
java.version = 25.0.4.1
4 virtual threads, 1 carrier, sleep(200) inside synchronized: OVERLAPPED (not pinned): about 200 ms
$ java -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1 PinningDemo (JDK 26)
java.version = 26.0.2.1
4 virtual threads, 1 carrier, sleep(200) inside synchronized: OVERLAPPED (not pinned): about 200 ms
$ java -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1 PinningDemo (JDK 27)
java.version = 27
4 virtual threads, 1 carrier, sleep(200) inside synchronized: OVERLAPPED (not pinned): about 200 ms
$ java -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1 PinningDemo (JDK 23, Corretto image)
java.version = 23.0.2
4 virtual threads, 1 carrier, sleep(200) inside synchronized: SERIALISED (pinned): about 4 x 200 ms
$ java -Djdk.virtualThreadScheduler.parallelism=1 -Djdk.virtualThreadScheduler.maxPoolSize=1 PinningDemo (JDK 24, Corretto image)
java.version = 24.0.2
4 virtual threads, 1 carrier, sleep(200) inside synchronized: OVERLAPPED (not pinned): about 200 ms
+26
View File
@@ -0,0 +1,26 @@
# -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version : generational ZGC is the only ZGC from Java 24 (JEP 490)
$ java -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version (JDK 21)
[0.004s][info][gc,init] Version: 21.0.12.1+9-LTS (release)
[0.004s][info][gc,init] Using legacy single-generation mode
$ java -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version (JDK 25)
OpenJDK 64-Bit Server VM warning: Ignoring option ZGenerational; support was removed in 24.0
[0.004s][info][gc,init] Version: 25.0.4.1+1-LTS (release)
$ java -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version (JDK 26)
Unrecognized VM option 'ZGenerational'
Error: Could not create the Java Virtual Machine.
$ java -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version (JDK 27)
Unrecognized VM option 'ZGenerational'
Error: Could not create the Java Virtual Machine.
$ java -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version (JDK 23, Corretto image)
OpenJDK 64-Bit Server VM warning: Option ZGenerational was deprecated in version 23.0 and will likely be removed in a future release.
[0.004s][info][gc,init] Version: 23.0.2+7-FR (release)
[0.004s][info][gc,init] Using deprecated non-generational mode
$ java -XX:+UseZGC -XX:-ZGenerational -Xlog:gc+init -version (JDK 24, Corretto image)
OpenJDK 64-Bit Server VM warning: Ignoring option ZGenerational; support was removed in 24.0
[0.005s][info][gc,init] Version: 24.0.2+12-FR (release)
+24
View File
@@ -0,0 +1,24 @@
# javap --module java.base -public 'java.util.concurrent.StructuredTaskScope$Joiner', static factories only
== JDK 25
public static <T> StructuredTaskScope$Joiner<T, Stream<StructuredTaskScope$Subtask<T>>> allSuccessfulOrThrow();
public static <T> StructuredTaskScope$Joiner<T, T> anySuccessfulResultOrThrow();
public static <T> StructuredTaskScope$Joiner<T, Void> awaitAllSuccessfulOrThrow();
public static <T> StructuredTaskScope$Joiner<T, Void> awaitAll();
public static <T> StructuredTaskScope$Joiner<T, Stream<StructuredTaskScope$Subtask<T>>> allUntil(Predicate<StructuredTaskScope$Subtask<? extends T>>);
== JDK 26
public static <T> StructuredTaskScope$Joiner<T, List<T>> allSuccessfulOrThrow();
public static <T> StructuredTaskScope$Joiner<T, T> anySuccessfulOrThrow();
public static <T> StructuredTaskScope$Joiner<T, Void> awaitAllSuccessfulOrThrow();
public static <T> StructuredTaskScope$Joiner<T, Void> awaitAll();
public static <T> StructuredTaskScope$Joiner<T, List<StructuredTaskScope$Subtask<T>>> allUntil(Predicate<StructuredTaskScope$Subtask<T>>);
== JDK 27
public static <T, R_X extends Throwable> StructuredTaskScope$Joiner<T, List<T>, R_X> allSuccessfulOrThrow(Function<Throwable, R_X>);
public static <T> StructuredTaskScope$Joiner<T, List<T>, ExecutionException> allSuccessfulOrThrow();
public static <T, R_X extends Throwable> StructuredTaskScope$Joiner<T, T, R_X> anySuccessfulOrThrow(Function<Throwable, R_X>);
public static <T> StructuredTaskScope$Joiner<T, T, ExecutionException> anySuccessfulOrThrow();
public static <T, R_X extends Throwable> StructuredTaskScope$Joiner<T, Void, R_X> awaitAllSuccessfulOrThrow(Function<Throwable, R_X>);
public static <T> StructuredTaskScope$Joiner<T, Void, ExecutionException> awaitAllSuccessfulOrThrow();
public static <T> StructuredTaskScope$Joiner<T, List<StructuredTaskScope$Subtask<T>>, RuntimeException> allUntil(Predicate<? super StructuredTaskScope$Subtask<T>>);
+26
View File
@@ -0,0 +1,26 @@
# _ (unnamed variables and patterns) and compact source files with instance main()
$ javac UnnamedVariables.java (JDK 21, no flag)
<repo>/lanes/src/UnnamedVariables.java:12: error: unnamed variables are a preview feature and are disabled by default.
if (o instanceof Point(int x, _)) {
^
(use --enable-preview to enable unnamed variables)
1 error
exit=1
$ javac UnnamedVariables.java (JDK 25, no flag)
exit=0
$ java UnnamedVariables (JDK 25)
matched a Point, x = 3, y ignored with _
caught NumberFormatException without naming it
$ java CompactHello.java (JDK 21, no flag)
<repo>/lanes/src/CompactHello.java:1: error: unnamed classes are a preview feature and are disabled by default.
void main() {
^
(use --enable-preview to enable unnamed classes)
1 error
error: compilation failed
$ java CompactHello.java (JDK 25, no flag)
compact source file: no class, no static, no String[] args
@@ -0,0 +1,11 @@
# Reflection writing to a final field on JDK 27: same behaviour as 26 (compare 70-final-field-mutation.txt)
=== JDK 27, defaults
WARNING: Final field port in class FinalFieldMutation$Config has been mutated reflectively by class FinalFieldMutation in unnamed module @<hash> (file:<repo>/build/lanes/ffm27/)
WARNING: Use --enable-final-field-mutation=ALL-UNNAMED to avoid a warning
WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabled
field reads : 9090
port() returns : 8080
=== JDK 27, --illegal-final-field-mutation=deny
setInt refused : class FinalFieldMutation (in unnamed module @<hash>) cannot set final field FinalFieldMutation$Config.port (in unnamed module @<hash>), unnamed module @<hash> is not allowed to mutate final fields
+40
View File
@@ -0,0 +1,40 @@
# pom.xml sets maven.compiler.source/target/release to 25 (lanes/upgrade/pom.xml)
$ mvn package (build JDK 21)
[INFO] --- resources:3.3.1:resources (default-resources) @ upgrade-demo ---
[INFO] --- compiler:3.13.0:compile (default-compile) @ upgrade-demo ---
[INFO] Compiling 1 source file with javac [debug release 25] to target/classes
[INFO] BUILD FAILURE
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.13.0:compile (default-compile) on project upgrade-demo: Fatal error compiling: error: release version 25 not supported -> [Help 1]
$ mvn package (build JDK 25)
[INFO] --- resources:3.3.1:resources (default-resources) @ upgrade-demo ---
[INFO] --- compiler:3.13.0:compile (default-compile) @ upgrade-demo ---
[INFO] Compiling 1 source file with javac [debug release 25] to target/classes
[INFO] --- resources:3.3.1:testResources (default-testResources) @ upgrade-demo ---
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ upgrade-demo ---
[INFO] --- surefire:3.2.5:test (default-test) @ upgrade-demo ---
[INFO] --- jar:3.4.1:jar (default-jar) @ upgrade-demo ---
[INFO] Building jar: <repo>/build/upgrade/mvn25/target/upgrade-demo-1.0.jar
[INFO] BUILD SUCCESS
$ mvn package (build JDK 27, still release 25)
[INFO] --- resources:3.3.1:resources (default-resources) @ upgrade-demo ---
[INFO] --- compiler:3.13.0:compile (default-compile) @ upgrade-demo ---
[INFO] Compiling 1 source file with javac [debug release 25] to target/classes
[INFO] --- resources:3.3.1:testResources (default-testResources) @ upgrade-demo ---
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ upgrade-demo ---
[INFO] --- surefire:3.2.5:test (default-test) @ upgrade-demo ---
[INFO] --- jar:3.4.1:jar (default-jar) @ upgrade-demo ---
[INFO] Building jar: <repo>/build/upgrade/mvn27/target/upgrade-demo-1.0.jar
[INFO] BUILD SUCCESS
$ javap -v -cp target/classes demo.App | grep 'major version'
major version: 69
$ java -cp target/classes demo.App (JDK 25)
running on Java 25
$ java -cp target/classes demo.App (JDK 21: the class file is newer than the JVM)
Error: LinkageError occurred while loading main class demo.App
java.lang.UnsupportedClassVersionError: demo/App has been compiled by a more recent version of the Java Runtime (class file version 69.0), this version of the Java Runtime only recognizes class file versions up to 65.0
+12
View File
@@ -0,0 +1,12 @@
# build.gradle.kts asks for a Java 25 toolchain (lanes/upgrade/build.gradle.kts). Two questions: which JDK compiles, and which JDK runs Gradle itself?
$ gradle run (Gradle 8.14.3 running on JDK 21, toolchain finds JDK 25)
running on Java 25
$ gradle run (Gradle 8.14.3 running on JDK 25)
FAILURE: Build failed with an exception.
* What went wrong:
25.0.4.1
$ gradle run (Gradle 9.1.0 running on JDK 25)
running on Java 25
+24
View File
@@ -0,0 +1,24 @@
# ReleaseFlag.java calls String.equalsFoldCase, which is new in Java 26. The compiler is JDK 27's javac both times.
$ javac --release 25 ReleaseFlag.java
<repo>/lanes/src/ReleaseFlag.java:8: error: cannot find symbol
System.out.println("ABC".equalsFoldCase("abc"));
^
symbol: method equalsFoldCase(String)
location: class String
1 error
exit=1
$ javac -source 25 -target 25 ReleaseFlag.java
warning: [options] location of system modules is not set in conjunction with -source 25
not setting the location of system modules may lead to class files that cannot run on JDK 25
--release 25 is recommended instead of -source 25 -target 25 because it sets the location of system modules automatically
1 warning
exit=0
$ java -cp rf-b ReleaseFlag (JDK 25)
Exception in thread "main" java.lang.NoSuchMethodError: 'boolean java.lang.String.equalsFoldCase(java.lang.String)'
at ReleaseFlag.main(ReleaseFlag.java:8)
$ java -cp rf-b ReleaseFlag (JDK 27)
true
+19
View File
@@ -0,0 +1,19 @@
# javac LombokProbe.java with -processorpath lombok-<version>.jar, then java LombokProbe. Class: @Getter on one field.
Lombok 1.18.34 on JDK 25: javac failed: java.lang.ExceptionInInitializerError
Lombok 1.18.34 on JDK 26: javac failed: java.lang.ExceptionInInitializerError
Lombok 1.18.34 on JDK 27: javac failed: java.lang.ExceptionInInitializerError
Lombok 1.18.40 on JDK 25: compiled; getName() = ankur
Lombok 1.18.40 on JDK 26: compiled; getName() = ankur
Lombok 1.18.40 on JDK 27: javac failed: java.lang.ExceptionInInitializerError
Lombok 1.18.46 on JDK 25: compiled; getName() = ankur
Lombok 1.18.46 on JDK 26: compiled; getName() = ankur
Lombok 1.18.46 on JDK 27: javac failed: java.lang.ExceptionInInitializerError
Lombok 1.18.48 on JDK 25: compiled; getName() = ankur
Lombok 1.18.48 on JDK 26: compiled; getName() = ankur
Lombok 1.18.48 on JDK 27: compiled; getName() = ankur
# The same source with the jar on the class path only (no -processorpath), the way a hand-run javac used to find it:
JDK 21, Lombok 1.18.48 on the class path only: exit=0 Note: Annotation processing is enabled because one or more processors were found
JDK 25, Lombok 1.18.48 on the class path only: exit=1 <repo>/lanes/src/LombokProbe.java:11: error: cannot find symbol
JDK 23, Lombok 1.18.48 on the class path only: exit=1 <repo>/lanes/src/LombokProbe.java:11: error: cannot find symbol
JDK 24, Lombok 1.18.48 on the class path only: exit=1 <repo>/lanes/src/LombokProbe.java:11: error: cannot find symbol
+15
View File
@@ -0,0 +1,15 @@
# jdeps --jdk-internals on the class from lanes/src/UnsafeWarning.java (JDK 25's jdeps)
$ jdeps --jdk-internals --multi-release 25 UnsafeWarning.class
UnsafeWarning.class -> jdk.unsupported
UnsafeWarning -> sun.misc.Unsafe JDK internal API (jdk.unsupported)
Warning: JDK internal APIs are unsupported and private to JDK implementation that are
subject to be removed or changed incompatibly and could break your application.
Please modify your code to eliminate dependence on any JDK internal APIs.
For the most recent update on JDK internal API replacements, please check:
https://wiki.openjdk.org/display/JDK8/Java+Dependency+Analysis+Tool
JDK Internal API Suggested Replacement
---------------- ---------------------
sun.misc.Unsafe See https://openjdk.org/jeps/260
+17
View File
@@ -0,0 +1,17 @@
# lanes/src/MockitoProbe.java: mock an interface, stub one call. Four Mockito versions, each with the ByteBuddy it declares.
Mockito 5.12.0 (ByteBuddy 1.14.15) on JDK 21: works: mock says hi
Mockito 5.12.0 (ByteBuddy 1.14.15) on JDK 25: FAILS: Mockito cannot mock this class: interface MockitoProbe$Greeter.
Mockito 5.12.0 (ByteBuddy 1.14.15) on JDK 27: FAILS: Mockito cannot mock this class: interface MockitoProbe$Greeter.
Mockito 5.17.0 (ByteBuddy 1.15.11) on JDK 21: works: mock says hi
Mockito 5.17.0 (ByteBuddy 1.15.11) on JDK 25: FAILS: Mockito cannot mock this class: interface MockitoProbe$Greeter.
Mockito 5.17.0 (ByteBuddy 1.15.11) on JDK 27: FAILS: Mockito cannot mock this class: interface MockitoProbe$Greeter.
Mockito 5.18.0 (ByteBuddy 1.17.5 ) on JDK 21: works: mock says hi
Mockito 5.18.0 (ByteBuddy 1.17.5 ) on JDK 25: works: mock says hi
Mockito 5.18.0 (ByteBuddy 1.17.5 ) on JDK 27: works: mock says hi
Mockito 5.23.0 (ByteBuddy 1.17.7 ) on JDK 21: works: mock says hi
Mockito 5.23.0 (ByteBuddy 1.17.7 ) on JDK 25: works: mock says hi
Mockito 5.23.0 (ByteBuddy 1.17.7 ) on JDK 27: works: mock says hi
# Notices on stderr from the working Mockito 5.23.0 on JDK 25
Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3
WARNING: A Java agent has been loaded dynamically (<repo>/build/upgrade/mockito/5.23.0/lib/byte-buddy-agent-1.17.7.jar)
+31
View File
@@ -0,0 +1,31 @@
import com.sun.management.HotSpotDiagnosticMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.stream.Collectors;
/**
* Prints which garbage collector the JVM picked when nobody asked for one, and why.
*
* <p>The "why" is the interesting part: HotSpot records the ORIGIN of every flag value.
* {@code ERGONOMIC} means the JVM chose it by looking at the machine; {@code DEFAULT} means
* it is simply the built-in value; {@code COMMAND_LINE} means somebody typed it.
*
* <p>Explained in docs/02-g1-default.md. Run by scripts/g1-default.sh.
*/
public class GcReport {
public static void main(String[] args) {
Runtime rt = Runtime.getRuntime();
System.out.println("java.version = " + System.getProperty("java.version"));
System.out.println("availableCpus = " + rt.availableProcessors());
System.out.println("maxHeapMB = " + rt.maxMemory() / (1024 * 1024));
System.out.println("collector beans = " + ManagementFactory.getGarbageCollectorMXBeans().stream()
.map(GarbageCollectorMXBean::getName).collect(Collectors.joining(", ")));
HotSpotDiagnosticMXBean hs = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
for (String flag : new String[] {"UseG1GC", "UseSerialGC", "UseParallelGC"}) {
var opt = hs.getVMOption(flag);
System.out.printf("%-17s = %-5s (origin %s)%n", flag, opt.getValue(), opt.getOrigin());
}
}
}
+67
View File
@@ -0,0 +1,67 @@
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* A deliberately boring allocation workload, used to ask one question: what does the switch from
* Serial to G1 cost (or save) on a machine that used to get Serial?
*
* <p>It allocates small objects as fast as it can, keeps roughly 1% of them alive in a ring buffer
* (so the collector has real work to do, not just an empty young generation), and reports wall
* time, collection count and time, the slowest single batch (a rough stand-in for the worst
* stall an application thread saw), and the process's peak resident memory from /proc.
*
* <p>Timing numbers are indicative, not a benchmark: one process, one run, a shared machine.
* The point is the SHAPE of the difference. See docs/02-g1-default.md.
*/
public class Workload {
record Item(long id, String label, byte[] payload) {}
public static void main(String[] args) throws Exception {
int batches = args.length > 0 ? Integer.parseInt(args[0]) : 400;
int perBatch = 50_000;
Item[] ring = new Item[100_000];
int ringPos = 0;
long checksum = 0;
long slowestBatchNanos = 0;
long start = System.nanoTime();
for (int b = 0; b < batches; b++) {
long t0 = System.nanoTime();
for (int i = 0; i < perBatch; i++) {
long id = (long) b * perBatch + i;
Item item = new Item(id, "item-" + id, new byte[64 + (int) (id & 63)]);
checksum += item.payload().length;
if (id % 100 == 0) {
ring[ringPos] = item;
ringPos = (ringPos + 1) % ring.length;
}
}
slowestBatchNanos = Math.max(slowestBatchNanos, System.nanoTime() - t0);
}
long elapsedMs = (System.nanoTime() - start) / 1_000_000;
long gcCount = 0, gcMs = 0;
for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
gcCount += Math.max(0, gc.getCollectionCount());
gcMs += Math.max(0, gc.getCollectionTime());
}
System.out.printf("allocated objects = %,d (checksum %d)%n", (long) batches * perBatch, checksum);
System.out.printf("wall time = %d ms%n", elapsedMs);
System.out.printf("gc count / time = %d / %d ms%n", gcCount, gcMs);
System.out.printf("slowest batch = %d ms%n", slowestBatchNanos / 1_000_000);
System.out.printf("peak RSS = %d MB%n", peakRssKb() / 1024);
}
/** VmHWM is the high-water mark of the resident set, in kB. */
static long peakRssKb() throws Exception {
for (String line : Files.readAllLines(Path.of("/proc/self/status"))) {
if (line.startsWith("VmHWM:")) {
return Long.parseLong(line.replaceAll("\\D+", ""));
}
}
return -1;
}
}
+15
View File
@@ -0,0 +1,15 @@
/**
* Tiny assertion helper so every demo is self-checking: a claim that stops being true turns the
* run red instead of quietly printing something different. Passing checks are echoed, so the
* transcript shows exactly what was verified.
*/
final class Check {
private Check() {}
static void that(boolean condition, String claim) {
if (!condition) {
throw new AssertionError("CHECK FAILED: " + claim);
}
System.out.println("CHECK ok : " + claim);
}
}
+45
View File
@@ -0,0 +1,45 @@
import java.io.DataInputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
/**
* How big is the TLS ClientHello this JDK sends?
*
* <p>A plain TCP server accepts the connection, reads the first TLS record header (5 bytes: type,
* version, 2-byte length) and then the record body, and reports the length. The client handshake
* never completes, and does not need to. Post-quantum key shares are large: this is the number that
* decides whether a hello still fits in one network packet. Explained in docs/04-post-quantum-tls.md.
*/
public class ClientHelloSize {
public static void main(String[] args) throws Exception {
try (ServerSocket listener = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) {
Thread client = new Thread(() -> {
try (SSLSocket s = (SSLSocket) SSLSocketFactory.getDefault()
.createSocket(InetAddress.getLoopbackAddress(), listener.getLocalPort())) {
s.setSoTimeout(1_000);
s.startHandshake(); // will time out: nobody answers
} catch (Exception expected) {
// the server side closes without replying
}
});
client.start();
try (Socket raw = listener.accept()) {
DataInputStream in = new DataInputStream(raw.getInputStream());
int type = in.readUnsignedByte();
int version = in.readUnsignedShort();
int length = in.readUnsignedShort();
byte[] body = new byte[length];
in.readFully(body);
System.out.printf("java.version = %s%n", System.getProperty("java.version"));
System.out.printf("record type = 0x%02x (0x16 = handshake)%n", type);
System.out.printf("ClientHello record = %d bytes (+5 byte record header)%n", length);
System.out.printf("fits one TCP segment = %s (1460-byte MSS on a 1500-byte MTU)%n", length + 5 <= 1460 ? "yes" : "NO");
}
client.join();
}
}
}
+6
View File
@@ -0,0 +1,6 @@
/** Does nothing for a second. Exists only so a JVM has arguments, properties and environment for JFR to record. */
public class Idle {
public static void main(String[] args) throws Exception {
Thread.sleep(1_000);
}
}
+94
View File
@@ -0,0 +1,94 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
/**
* JEP 531, Lazy Constants (third preview), against the JDK 27 API.
*
* <p>A lazy constant is a value that is computed the first time somebody asks for it and never again,
* even when many threads ask at once. The JVM can then treat it like a {@code final} field. What changed
* since JDK 26: {@code orElse(...)} and {@code isInitialized()} are gone (see broken/Lazy26.java), and
* {@code Set.ofLazy(...)} arrived next to {@code List.ofLazy} and {@code Map.ofLazy}.
* Explained in docs/06-lazy-constants.md. Compile and run with --enable-preview.
*/
public class LazyDemo {
static final AtomicInteger INITIALISATIONS = new AtomicInteger();
/** The expensive thing: pretend this reads a file and parses it. */
record Settings(String region, int poolSize) {}
static Settings loadSettings() {
INITIALISATIONS.incrementAndGet();
System.out.println(" (loading settings on " + Thread.currentThread().getName() + ")");
return new Settings("ap-south-1", 16);
}
static final LazyConstant<Settings> SETTINGS = LazyConstant.of(LazyDemo::loadSettings);
/** Identity hashes and lambda addresses change on every run; blank them so the transcript is stable. */
static String tidy(Object o) {
return o.toString().replaceAll("@[0-9a-f]+", "@...").replaceAll("/0x[0-9a-f]+", "/0x...");
}
public static void main(String[] args) throws Exception {
System.out.println("--- a lazy constant computes once, on first use");
System.out.println("toString before first get(): " + tidy(SETTINGS));
Settings first = SETTINGS.get();
Settings second = SETTINGS.get();
System.out.println("toString after first get(): " + tidy(SETTINGS));
Check.that(first == second, "get() returns the same instance every time");
Check.that(INITIALISATIONS.get() == 1, "the supplier ran exactly once");
System.out.println("--- and once even when 64 threads race for it");
var raced = LazyConstant.of(() -> {
INITIALISATIONS.incrementAndGet();
return "computed";
});
int before = INITIALISATIONS.get();
var start = new CountDownLatch(1);
try (var pool = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 64; i++) {
pool.submit(() -> {
start.await();
return raced.get();
});
}
start.countDown();
}
Check.that(INITIALISATIONS.get() - before == 1, "64 racing threads triggered exactly one initialisation");
System.out.println("--- List.ofLazy: each element is computed on first access");
AtomicInteger listComputations = new AtomicInteger();
List<String> squares = List.ofLazy(5, i -> {
listComputations.incrementAndGet();
return "square(" + i + ")=" + (i * i);
});
System.out.println("computed so far: " + listComputations.get());
System.out.println("squares.get(3) = " + squares.get(3));
System.out.println("squares.get(3) = " + squares.get(3) + " (second read)");
Check.that(listComputations.get() == 1, "only element 3 was ever computed");
System.out.println("--- Map.ofLazy: the keys are fixed, the values are computed on first access");
AtomicInteger mapComputations = new AtomicInteger();
Map<String, Integer> lengths = Map.ofLazy(Set.of("alpha", "beta", "gamma"), k -> {
mapComputations.incrementAndGet();
return k.length();
});
System.out.println("lengths.get(\"gamma\") = " + lengths.get("gamma"));
Check.that(mapComputations.get() == 1, "only the requested key's value was computed");
System.out.println("--- Set.ofLazy (new in 27): membership is decided by a predicate, on demand");
AtomicInteger setComputations = new AtomicInteger();
Set<Integer> primes = Set.ofLazy(Set.of(2, 3, 4, 5, 6, 7), n -> {
setComputations.incrementAndGet();
return n > 1 && java.util.stream.IntStream.rangeClosed(2, (int) Math.sqrt(n)).noneMatch(d -> n % d == 0);
});
System.out.println("primes.contains(7) = " + primes.contains(7) + ", primes.contains(6) = " + primes.contains(6));
Check.that(primes.contains(7) && !primes.contains(6), "the lazy set answers membership using the predicate");
System.out.println("primes = " + new java.util.TreeSet<>(primes));
}
}
+98
View File
@@ -0,0 +1,98 @@
import java.nio.charset.StandardCharsets;
import java.security.BinaryEncodable;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PEM;
import java.security.PEMDecoder;
import java.security.PEMEncoder;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.EncryptedPrivateKeyInfo;
import java.util.Arrays;
import java.util.Base64;
/**
* JEP 538, PEM Encodings of Cryptographic Objects (third preview), against the JDK 27 API.
*
* <p>PEM is the "-----BEGIN ...-----" text format every key and certificate file on a Linux box is in.
* Before this API, turning a key into PEM or back meant hand-rolled Base64 and header strings.
* Note the interface name: JDK 26 called the common supertype {@code DEREncodable}; JDK 27 renamed it
* {@code BinaryEncodable}. See broken/Pem26Style.java. Explained in docs/08-pem-api.md.
* Compile and run with --enable-preview.
*/
public class PemDemo {
public static void main(String[] args) throws Exception {
KeyPairGenerator gen = KeyPairGenerator.getInstance("EC");
gen.initialize(256);
KeyPair pair = gen.generateKeyPair();
// 1. Encode: one call, no Base64 in sight.
String publicPem = PEMEncoder.of().encodeToString(pair.getPublic());
String privatePem = PEMEncoder.of().encodeToString(pair.getPrivate());
System.out.println("--- public key, encoded");
System.out.println(firstLines(publicPem, 1) + " ... " + lastLine(publicPem));
System.out.println("--- private key, encoded");
System.out.println(firstLines(privatePem, 1) + " ... " + lastLine(privatePem));
Check.that(publicPem.startsWith("-----BEGIN PUBLIC KEY-----"), "a PublicKey encodes as BEGIN PUBLIC KEY");
Check.that(privatePem.startsWith("-----BEGIN PRIVATE KEY-----"), "a PrivateKey encodes as BEGIN PRIVATE KEY (PKCS#8)");
// 2. Decode, asking for the type you expect.
PublicKey publicBack = PEMDecoder.of().decode(publicPem, PublicKey.class);
PrivateKey privateBack = PEMDecoder.of().decode(privatePem, PrivateKey.class);
Check.that(publicBack.equals(pair.getPublic()), "the decoded public key equals the original");
Check.that(privateBack.equals(pair.getPrivate()), "the decoded private key equals the original");
// 3. Decode without saying what you expect, and switch on what came back.
System.out.println("--- decode(String) returns whatever the header says it is");
for (String pem : new String[] {publicPem, privatePem}) {
BinaryEncodable decoded = PEMDecoder.of().decode(pem);
String kind = switch (decoded) {
case PublicKey k -> "PublicKey (" + k.getAlgorithm() + ")";
case PrivateKey k -> "PrivateKey (" + k.getAlgorithm() + ")";
default -> decoded.getClass().getName();
};
System.out.println(pem.lines().findFirst().orElse("") + " -> " + kind);
}
// 4. Encrypted private keys: the password goes on the encoder and the decoder.
char[] password = "correct horse".toCharArray();
String encryptedPem = PEMEncoder.of().withEncryption(password).encodeToString(pair.getPrivate());
System.out.println("--- encrypted private key");
System.out.println(firstLines(encryptedPem, 1));
Check.that(encryptedPem.startsWith("-----BEGIN ENCRYPTED PRIVATE KEY-----"), "withEncryption produces ENCRYPTED PRIVATE KEY");
BinaryEncodable withoutPassword = PEMDecoder.of().decode(encryptedPem);
System.out.println("decoded with no password: " + withoutPassword.getClass().getName());
Check.that(withoutPassword instanceof EncryptedPrivateKeyInfo, "without a password you get the still-encrypted structure back");
PrivateKey decrypted = PEMDecoder.of().withDecryption(password).decode(encryptedPem, PrivateKey.class);
Check.that(decrypted.equals(pair.getPrivate()), "with the password you get the original private key");
// 5. A PEM type the JDK does not know: it round-trips as a raw PEM object.
// The trap: PEM's content is the Base64 TEXT, not the payload. Hand it raw bytes and they are written verbatim.
byte[] payload = "hello, pem".getBytes(StandardCharsets.UTF_8);
String wrong = PEMEncoder.of().encodeToString(new PEM("ANKURM DEMO", payload));
System.out.println("--- PEM(type, byte[]) does NOT Base64-encode: the bytes go in as they are");
System.out.println(wrong.strip());
Check.that(wrong.contains("hello, pem"), "raw bytes appear verbatim between the header and footer");
String base64 = Base64.getEncoder().encodeToString(payload);
String right = PEMEncoder.of().encodeToString(new PEM("ANKURM DEMO", base64));
System.out.println("--- give it the Base64 text and it round-trips");
System.out.println(right.strip());
BinaryEncodable raw = PEMDecoder.of().decode(right);
Check.that(raw instanceof PEM p && p.type().equals("ANKURM DEMO")
&& new String(p.content(), StandardCharsets.US_ASCII).strip().equals(base64)
&& Arrays.equals(p.decode(), payload),
"type, Base64 text (content()) and decoded payload (decode()) all survive");
}
static String firstLines(String s, int n) {
return s.lines().limit(n).reduce((a, b) -> a + "\n" + b).orElse("");
}
static String lastLine(String s) {
return s.lines().reduce((a, b) -> b).orElse("");
}
}
+95
View File
@@ -0,0 +1,95 @@
/**
* JEP 532, Primitive Types in Patterns, instanceof, and switch (fifth preview).
*
* <p>Until now patterns worked on reference types only. With this preview a pattern can name a
* primitive type, and the test is "can this value be converted to that type WITHOUT losing
* information?" Explained in docs/05-primitive-patterns.md. Compile and run with --enable-preview.
*/
public class PrimitivePatterns {
record Reading(int celsius) {}
public static void main(String[] args) {
exactness();
switchOnLong();
switchOnObject();
recordPattern();
}
/** instanceof with a primitive type asks: is this an EXACT conversion? */
static void exactness() {
System.out.println("--- instanceof <primitive>: an exact-conversion test, not a cast");
int small = 100, big = 300;
boolean a = small instanceof byte;
boolean b = big instanceof byte;
System.out.println("100 instanceof byte = " + a);
System.out.println("300 instanceof byte = " + b + " (a byte holds -128..127)");
Check.that(a && !b, "100 converts to byte exactly, 300 does not");
if (small instanceof byte narrow) {
System.out.println("binding: byte narrow = " + narrow);
}
int precise = 16_777_217; // 2^24 + 1: the first int a float cannot hold
boolean f1 = 16_777_216 instanceof float;
boolean f2 = precise instanceof float;
System.out.println("16_777_216 instanceof float = " + f1);
System.out.println("16_777_217 instanceof float = " + f2 + " (float has a 24-bit significand)");
Check.that(f1 && !f2, "int to float is exact up to 2^24 and lossy after");
}
/** switch over a long, with a primitive pattern and a guard. */
static String classify(long value) {
return switch (value) {
case 0L -> "zero";
case 1L -> "one";
case long v when v < 0 -> "negative (" + v + ")";
case long v -> "positive (" + v + ")";
};
}
static void switchOnLong() {
System.out.println("--- switch on a long, with pattern labels and a guard");
for (long v : new long[] {0, 1, -7, 9_000_000_000L}) {
System.out.println("classify(" + v + ") = " + classify(v));
}
Check.that(classify(0).equals("zero") && classify(-7).startsWith("negative"), "long switch dispatches on constants, then patterns");
}
/** switch over an Object: primitive patterns match the boxed types they correspond to. */
static String describe(Object o) {
return switch (o) {
case int i when i > 1000 -> "a large int " + i;
case int i -> "an int " + i;
case long l -> "a long " + l;
case double d -> "a double " + d;
case String s -> "a String of length " + s.length();
default -> "something else: " + o;
};
}
static void switchOnObject() {
System.out.println("--- switch on an Object: an Integer matches 'case int'");
Object[] samples = {7, 5000, 7L, 2.5, "hello", 'x'};
for (Object o : samples) {
System.out.println("describe(" + o.getClass().getSimpleName() + " " + o + ") = " + describe(o));
}
Check.that(describe(5000).equals("a large int 5000"), "an Integer 5000 matches case int with a guard");
}
/** Record patterns can now narrow a component: Reading.celsius is an int, the pattern asks for a byte. */
static void recordPattern() {
System.out.println("--- record pattern with a narrowing primitive component");
Object[] readings = {new Reading(36), new Reading(4_000)};
for (Object o : readings) {
String verdict = switch (o) {
case Reading(byte c) -> "fits in a byte: " + c;
case Reading(int c) -> "needs a full int: " + c;
default -> "not a reading";
};
System.out.println(o + " -> " + verdict);
}
Check.that(readings[0] instanceof Reading(byte _) && !(readings[1] instanceof Reading(byte _)),
"Reading(byte c) matches 36 but not 4000");
}
}
+102
View File
@@ -0,0 +1,102 @@
import java.time.Duration;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructuredTaskScope.Joiner;
import java.util.concurrent.StructuredTaskScope.Subtask;
/**
* JEP 533, Structured Concurrency (seventh preview), written against the JDK 27 API.
*
* <p>The headline change from JDK 26 is that {@code StructuredTaskScope} and {@code Joiner} gained a
* third type parameter, {@code R_X}: the exception {@code join()} throws. That lets a scope throw YOUR
* exception type rather than a generic {@code FailedException}. Compare broken/StructuredScope26.java,
* which is JDK 26 code that no longer compiles. Explained in docs/07-structured-concurrency.md.
*
* <p>Compile and run with --enable-preview.
*/
public class StructuredDemo {
/** The application's own failure type: what R_X lets us surface. */
static class OrderFailed extends Exception {
OrderFailed(Throwable cause) {
super("order failed: " + cause.getMessage(), cause);
}
}
static String fetchPrice() throws InterruptedException {
Thread.sleep(50);
return "price=42";
}
static String fetchStock() throws InterruptedException {
Thread.sleep(80);
return "stock=7";
}
static String failingCall() {
throw new IllegalStateException("inventory service is down");
}
public static void main(String[] args) throws Exception {
allSuccessful();
yourOwnExceptionType();
timeout();
}
/** The default joiner: all subtasks must succeed, failure surfaces as ExecutionException. */
static void allSuccessful() throws Exception {
System.out.println("--- allSuccessfulOrThrow(): results as a List, failures as ExecutionException");
try (var scope = StructuredTaskScope.open(Joiner.<String>allSuccessfulOrThrow())) {
scope.fork(StructuredDemo::fetchPrice);
scope.fork(StructuredDemo::fetchStock);
List<String> results = scope.join();
System.out.println("results = " + results);
Check.that(results.equals(List.of("price=42", "stock=7")), "join() returns the subtask results in fork order");
}
try (var scope = StructuredTaskScope.open(Joiner.<String>allSuccessfulOrThrow())) {
scope.fork(StructuredDemo::fetchPrice);
scope.fork(StructuredDemo::failingCall);
scope.join();
Check.that(false, "unreachable");
} catch (ExecutionException e) {
System.out.println("caught " + e.getClass().getName() + " with cause " + e.getCause());
Check.that(e.getCause() instanceof IllegalStateException, "a failing subtask surfaces as ExecutionException(cause)");
}
}
/** New in 27: hand the joiner a function and join() throws YOUR type. */
static void yourOwnExceptionType() throws InterruptedException {
System.out.println("--- allSuccessfulOrThrow(Function): join() throws the exception type you choose");
try (var scope = StructuredTaskScope.open(Joiner.<String, OrderFailed>allSuccessfulOrThrow(OrderFailed::new))) {
scope.fork(StructuredDemo::fetchPrice);
scope.fork(StructuredDemo::failingCall);
scope.join(); // declared: throws OrderFailed, InterruptedException
Check.that(false, "unreachable");
} catch (OrderFailed e) {
System.out.println("caught " + e.getClass().getSimpleName() + ": " + e.getMessage());
Check.that(e.getCause() instanceof IllegalStateException, "the mapped exception wraps the original failure");
}
}
/** Timeouts are now a configuration option; a Joiner decides what a timeout means. */
static void timeout() throws Exception {
System.out.println("--- withTimeout(...): what happens when the scope runs out of time");
try (var scope = StructuredTaskScope.open(Joiner.<String>allSuccessfulOrThrow(),
cfg -> cfg.withTimeout(Duration.ofMillis(100)))) {
Subtask<String> slow = scope.fork(() -> {
Thread.sleep(2_000);
return "too late";
});
scope.join();
Check.that(false, "unreachable");
} catch (Exception e) {
System.out.println("caught " + e.getClass().getName());
System.out.println(" cause: " + e.getCause());
Check.that(e.getCause() instanceof StructuredTaskScope.CancelledByTimeoutException
|| e instanceof StructuredTaskScope.CancelledByTimeoutException,
"a timeout is reported as CancelledByTimeoutException");
}
}
}
+44
View File
@@ -0,0 +1,44 @@
import java.io.InputStream;
import java.net.InetAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
/**
* One end of a loopback TLS 1.3 handshake, so two different JDKs can be pointed at each other.
*
* <pre>
* java TlsPeer server &lt;portfile&gt; listen on an ephemeral port, write the port to a file, serve one handshake
* java TlsPeer client &lt;port&gt; connect and complete a handshake
* </pre>
*
* The scripts run both with -Djavax.net.debug=ssl:handshake and read the negotiated key-exchange group
* out of the debug log; no JDK API reports it directly. Explained in docs/04-post-quantum-tls.md.
*/
public class TlsPeer {
public static void main(String[] args) throws Exception {
if (args[0].equals("server")) {
SSLServerSocket server = (SSLServerSocket) SSLServerSocketFactory.getDefault()
.createServerSocket(0, 1, InetAddress.getLoopbackAddress());
Files.writeString(Path.of(args[1]), Integer.toString(server.getLocalPort()));
try (SSLSocket socket = (SSLSocket) server.accept()) {
socket.startHandshake();
socket.getOutputStream().write('!');
}
server.close();
} else {
SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault()
.createSocket(InetAddress.getLoopbackAddress(), Integer.parseInt(args[1]));
socket.startHandshake();
System.out.println("handshake complete: " + socket.getSession().getProtocol()
+ " " + socket.getSession().getCipherSuite());
InputStream in = socket.getInputStream();
in.read();
socket.close();
}
}
}
+53
View File
@@ -0,0 +1,53 @@
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorOperators;
import jdk.incubator.vector.VectorSpecies;
/**
* JEP 537, Vector API (twelfth incubator). The same source and the same class file run on JDK 26
* (eleventh incubator, JEP 529) and JDK 27, because the public API did not change between them:
* docs/output/50-vector-api-surface.txt is the javap diff that shows it. For what the API is and
* how to use it well, see the companion guide to JEP 537 on ankurm.com.
* Run with --add-modules jdk.incubator.vector.
*/
public class VectorDemo {
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
static float scalarDot(float[] a, float[] b) {
float sum = 0;
for (int i = 0; i < a.length; i++) {
sum += a[i] * b[i];
}
return sum;
}
static float vectorDot(float[] a, float[] b) {
int i = 0;
FloatVector acc = FloatVector.zero(SPECIES);
int bound = SPECIES.loopBound(a.length);
for (; i < bound; i += SPECIES.length()) {
acc = FloatVector.fromArray(SPECIES, a, i).fma(FloatVector.fromArray(SPECIES, b, i), acc);
}
float sum = acc.reduceLanes(VectorOperators.ADD);
for (; i < a.length; i++) { // the tail the vector loop could not cover
sum += a[i] * b[i];
}
return sum;
}
public static void main(String[] args) {
System.out.println("java.version = " + System.getProperty("java.version"));
System.out.println("preferred species = " + SPECIES + " (" + SPECIES.length() + " float lanes, machine dependent)");
int n = 1_000_003; // deliberately not a multiple of the lane count
float[] a = new float[n], b = new float[n];
for (int i = 0; i < n; i++) {
a[i] = (i % 7) * 0.5f;
b[i] = (i % 5) * 0.25f;
}
float scalar = scalarDot(a, b);
float vector = vectorDot(a, b);
System.out.println("scalar dot = " + scalar);
System.out.println("vector dot = " + vector);
// Floating-point addition is not associative, so the two sums differ in the last bits; compare with a tolerance.
Check.that(Math.abs(scalar - vector) / Math.abs(scalar) < 1e-3, "vector and scalar dot products agree to within 0.1%");
}
}
+13
View File
@@ -0,0 +1,13 @@
/**
* A primitive pattern placed after the reference pattern that already matches every Integer.
* The compiler rejects it on both 26 and 27. Driven by scripts/broken-on-27.sh.
*/
public class Dominated {
static String describe(Object o) {
return switch (o) {
case Integer boxed -> "an Integer";
case int primitive -> "an int"; // dominated: every Integer is already caught above
default -> "something else";
};
}
}
+17
View File
@@ -0,0 +1,17 @@
import java.lang.LazyConstant;
/**
* JDK 26 code for the lazy constants preview. It compiles on 26 and fails on 27:
* LazyConstant.orElse(...) and LazyConstant.isInitialized() were removed in the third preview.
* Driven by scripts/broken-on-27.sh.
*/
public class Lazy26 {
static final LazyConstant<String> GREETING = LazyConstant.of(() -> "hello");
public static void main(String[] args) {
if (!GREETING.isInitialized()) {
System.out.println("not yet: " + GREETING.orElse("<unset>"));
}
System.out.println(GREETING.get());
}
}
+26
View File
@@ -0,0 +1,26 @@
import java.security.DEREncodable;
import java.security.PEM;
import java.security.PEMDecoder;
import java.security.Provider;
import java.security.Security;
/**
* JDK 26 code for the PEM preview API. It compiles on 26 and fails on 27.
* Three things moved between the second and third preview (JEP 524 -> JEP 538):
* 1. DEREncodable was renamed BinaryEncodable
* 2. PEMDecoder.withFactory(Provider) became withFactoriesOf(Provider)
* 3. PEM was a record whose content() returned a String; it is now a class whose content() returns byte[]
* Driven by scripts/broken-on-27.sh, which commits the compiler's own messages.
*/
public class Pem26Style {
public static void main(String[] args) {
String text = "-----BEGIN ANKURM DEMO-----\naGVsbG8=\n-----END ANKURM DEMO-----\n";
DEREncodable decoded = PEMDecoder.of().decode(text); // 1
Provider provider = Security.getProviders()[0];
PEMDecoder viaProvider = PEMDecoder.of().withFactory(provider); // 2
if (decoded instanceof PEM pem) {
String base64Text = pem.content(); // 3
System.out.println(pem.type() + " / " + base64Text);
}
}
}
@@ -0,0 +1,23 @@
import java.util.List;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.StructuredTaskScope.Joiner;
/**
* JDK 26 code for the structured concurrency preview. It compiles on 26 and fails on 27:
* StructuredTaskScope gained a third type parameter (the exception join() throws), and
* FailedException / TimeoutException no longer exist. Driven by scripts/broken-on-27.sh.
*/
public class StructuredScope26 {
public static void main(String[] args) throws Exception {
try (StructuredTaskScope<String, List<String>> scope =
StructuredTaskScope.open(Joiner.<String>allSuccessfulOrThrow())) {
scope.fork(() -> "price=42");
List<String> results = scope.join();
System.out.println(results);
} catch (StructuredTaskScope.FailedException e) {
System.out.println("a subtask failed: " + e.getCause());
} catch (StructuredTaskScope.TimeoutException e) {
System.out.println("timed out");
}
}
}
+1
View File
@@ -0,0 +1 @@
Review the attached Java class and convert it to a Java 21 record if it is a pure data carrier. Preserve all validation from the old constructor inside a compact constructor. Flag fields that prevent conversion (e.g. mutable state, inheritance) and explain why.
@@ -0,0 +1 @@
Refactor this Java code to use Java 21 pattern matching for switch. Introduce a sealed interface if the types form a closed hierarchy. Ensure the switch is exhaustive and remove all redundant casts and intermediate variables.
@@ -0,0 +1 @@
Find every usage of Executors.newFixedThreadPool and Executors.newCachedThreadPool in this project. Replace with Executors.newVirtualThreadPerTaskExecutor() where the tasks are I/O-bound. List any cases where platform threads should be kept (CPU-bound work, native frames on the stack, and on Java 21 to 23 also synchronized blocks that block, which pin the carrier thread until Java 24) and justify each decision.
@@ -0,0 +1 @@
Rewrite this code to use Java 21 sequenced collection APIs (getFirst, getLast, reversed) instead of list.get(0), list.get(list.size()-1), and manual reversal. Keep behaviour identical.
@@ -0,0 +1 @@
Identify ThreadLocal usages in this file. Propose a migration to Java 25 ScopedValue, showing before/after code and explaining why ScopedValue is safer under virtual threads and structured concurrency.
@@ -0,0 +1 @@
Refactor this CompletableFuture.allOf pipeline to use StructuredTaskScope. Note that StructuredTaskScope is still a preview API in Java 25, 26 and 27, so the build needs --enable-preview and the code must use the factory-method names of the JDK it targets (Joiner.anySuccessfulResultOrThrow() on 25, anySuccessfulOrThrow() on 26 and 27). Preserve error propagation semantics and show what happens on cancellation. Explain the difference from the original CompletableFuture approach.
+1
View File
@@ -0,0 +1 @@
Here is my pom.xml targeting Java 21. Update it to target Java 25. Bump Spring Boot to the latest 3.5.x or 4.x release (3.4.x is documented only up to Java 24), Hibernate to 7.x (which needs Jakarta Persistence 3.2), Lombok to 1.18.40 or later, JUnit to 6.x, and Mockito to its current 5.x release. List any dependency that does not have a Java 25 compatible version and suggest alternatives.
@@ -0,0 +1 @@
Scan this Java file for usages of SecurityManager, Thread.stop/suspend/resume, Object.finalize, sun.misc.Unsafe memory methods, System.loadLibrary and other JNI loading, and the old java.net.URL constructors. For each finding, say which Java release changed it (SecurityManager: cannot be installed since 24; Thread.suspend/resume: still compile on 21, gone by 23; Thread.stop: throws UnsupportedOperationException on 21 and 25, removed in 26; finalize: still runs, deprecated for removal; Unsafe memory access and JNI loading: run-time warnings from 24) and give a replacement with a before/after code snippet.
@@ -0,0 +1 @@
Review the attached Maven/Gradle build file and list every --enable-preview compiler and runtime argument. For each one, name the preview feature the code actually uses. Remove the flag only if that feature is final in Java 25 (scoped values, JEP 506, are final in 25; StructuredTaskScope is still a preview API in 25, 26 and 27, so a project that uses it must keep the flag). Explain each decision.
@@ -0,0 +1 @@
Apply these Java 25 idioms to this file: records for data carriers, pattern-matching switch for type checks, sequenced collection APIs (getFirst/getLast), and ScopedValue instead of ThreadLocal. Preserve behaviour exactly and add a comment for each change explaining why the new form is preferred.

Some files were not shown because too many files have changed in this diff Show More