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:
@@ -0,0 +1,4 @@
|
||||
build/
|
||||
*.class
|
||||
*.jfr
|
||||
.lib/
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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()
|
||||
@@ -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')
|
||||
@@ -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)
|
||||
@@ -0,0 +1,56 @@
|
||||
# 2. G1 becomes the default, even on one CPU (JEP 523)
|
||||
|
||||
Prev: [1. What shipped](01-what-shipped.md) · 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) · Next: [3. Compact object headers](03-compact-headers.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
# 3. Compact object headers are the default (JEP 534)
|
||||
|
||||
Prev: [2. G1](02-g1-default.md) · 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) · Next: [4. Post-quantum TLS](04-post-quantum-tls.md)
|
||||
@@ -0,0 +1,36 @@
|
||||
# 4. Post-quantum hybrid key exchange in TLS 1.3 (JEP 527)
|
||||
|
||||
Prev: [3. Compact headers](03-compact-headers.md) · 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) · Next: [5. Primitive patterns](05-primitive-patterns.md)
|
||||
@@ -0,0 +1,32 @@
|
||||
# 5. Primitive types in patterns (JEP 532, fifth preview)
|
||||
|
||||
Prev: [4. Post-quantum TLS](04-post-quantum-tls.md) · 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) · Next: [6. Lazy constants](06-lazy-constants.md)
|
||||
@@ -0,0 +1,23 @@
|
||||
# 6. Lazy constants (JEP 531, third preview)
|
||||
|
||||
Prev: [5. Primitive patterns](05-primitive-patterns.md) · 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) · Next: [7. Structured concurrency](07-structured-concurrency.md)
|
||||
@@ -0,0 +1,27 @@
|
||||
# 7. Structured concurrency (JEP 533, seventh preview)
|
||||
|
||||
Prev: [6. Lazy constants](06-lazy-constants.md) · 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) · Next: [8. PEM](08-pem-api.md)
|
||||
@@ -0,0 +1,27 @@
|
||||
# 8. PEM encodings (JEP 538, third preview)
|
||||
|
||||
Prev: [7. Structured concurrency](07-structured-concurrency.md) · 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) · Next: [9. JFR redaction and the Vector API](09-jfr-redaction-and-vector.md)
|
||||
@@ -0,0 +1,28 @@
|
||||
# 9. JFR redaction (JEP 536) and the Vector API (JEP 537)
|
||||
|
||||
Prev: [8. PEM](08-pem-api.md) · 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) · Next: [10. Other 27 changes](10-other-27-changes.md)
|
||||
@@ -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) · 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) · Next: [11. Recap of Java 26](11-recap-26.md)
|
||||
@@ -0,0 +1,68 @@
|
||||
# 11. Recap: the Java 26 changes you skipped
|
||||
|
||||
Prev: [10. Other 27 changes](10-other-27-changes.md) · 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) · Next: [12. Version lanes 25 - 29](12-lanes-25-to-29.md)
|
||||
@@ -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) · Next: [13. Upgrade checklist](13-upgrade-checklist.md) · 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) · Next: [13. Upgrade checklist](13-upgrade-checklist.md) · Coming from 21: [14](14-lanes-21-to-25.md)
|
||||
@@ -0,0 +1,18 @@
|
||||
# 13. Upgrade checklist: 25 to 27 (or to 26)
|
||||
|
||||
Prev: [12. Version lanes](12-lanes-25-to-29.md) · Next: [14. The 21 to 25 lane](14-lanes-21-to-25.md) · [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) · [Back to the README](../README.md)
|
||||
@@ -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) · [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) · [Back to the README](../README.md)
|
||||
@@ -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) · [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) · [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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
@@ -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.
|
||||
@@ -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')
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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>>);
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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("");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <portfile> listen on an ephemeral port, write the port to a file, serve one handshake
|
||||
* java TlsPeer client <port> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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%");
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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
Reference in New Issue
Block a user