From f59c1de96d345bb095929a083cfe5e887ef3764b Mon Sep 17 00:00:00 2001 From: Ankur Mhatre Date: Mon, 21 Sep 2026 13:55:46 +0000 Subject: [PATCH] Java 27 and 26: runnable demos and captured output for every JEP, plus version lanes Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01B38FGKKam5SCGgwgduVAh3 --- .gitignore | 4 + LICENSE | 21 ++ README.md | 75 +++++++ api-diff/apidiff.py | 74 +++++++ api-diff/dump_api.py | 33 +++ docs/01-what-shipped.md | 52 +++++ docs/02-g1-default.md | 56 +++++ docs/03-compact-headers.md | 36 ++++ docs/04-post-quantum-tls.md | 36 ++++ docs/05-primitive-patterns.md | 32 +++ docs/06-lazy-constants.md | 23 ++ docs/07-structured-concurrency.md | 27 +++ docs/08-pem-api.md | 27 +++ docs/09-jfr-redaction-and-vector.md | 28 +++ docs/10-other-27-changes.md | 25 +++ docs/11-recap-26.md | 68 ++++++ docs/12-lanes-25-to-29.md | 31 +++ docs/13-upgrade-checklist.md | 18 ++ docs/14-lanes-21-to-25.md | 40 ++++ docs/15-build-files.md | 61 ++++++ docs/output/01-g1-default-1cpu-vs-2cpu.txt | 43 ++++ docs/output/02-g1-default-memory.txt | 43 ++++ docs/output/03-g1-default-optout.txt | 11 + docs/output/04-g1-cost-1cpu.txt | 51 +++++ docs/output/05-g1-cost-1cpu-short.txt | 51 +++++ docs/output/06-g1-tuning-1cpu.txt | 41 ++++ docs/output/10-headers-jdk26.txt | 82 ++++++++ docs/output/11-headers-jdk27.txt | 79 +++++++ docs/output/12-headers-jdk27-opt-out.txt | 86 ++++++++ docs/output/13-jol-record-failure.txt | 11 + docs/output/14-jol-record-workaround.txt | 9 + docs/output/15-headers-summary.txt | 31 +++ docs/output/20-broken-dominated.txt | 6 + docs/output/20-broken-lazy26.txt | 16 ++ docs/output/20-broken-pem26style.txt | 19 ++ docs/output/20-broken-structuredscope26.txt | 19 ++ docs/output/21-structured.txt | 14 ++ docs/output/22-lazy.txt | 22 ++ docs/output/23-primitive-patterns.txt | 28 +++ docs/output/24-pem.txt | 29 +++ docs/output/25-vector.txt | 7 + docs/output/30-tls-groups-matrix.txt | 21 ++ docs/output/31-tls-clienthello-size.txt | 19 ++ docs/output/32-tls-opt-out.txt | 6 + docs/output/40-jfr-redaction.txt | 41 ++++ docs/output/50-vector-api-surface.txt | 8 + docs/output/51-api-diff-26-to-27.txt | 190 +++++++++++++++++ docs/output/52-api-diff-25-to-26.txt | 197 ++++++++++++++++++ docs/output/60-removed-options.txt | 54 +++++ docs/output/61-behaviour-changes.txt | 21 ++ docs/output/62-new-api.txt | 12 ++ docs/output/70-final-field-mutation.txt | 45 ++++ docs/output/71-aot-cache-matrix.txt | 20 ++ docs/output/72-aot-startup.txt | 7 + docs/output/73-removed-in-26.txt | 34 +++ docs/output/74-api26.txt | 12 ++ docs/output/75-http3-fallback.txt | 26 +++ docs/output/80-hub-examples.txt | 69 ++++++ docs/output/81-scoped-values.txt | 41 ++++ docs/output/82-structured-not-final.txt | 117 +++++++++++ docs/output/83-finalization.txt | 29 +++ docs/output/84-security-manager.txt | 9 + docs/output/85-thread-stop-suspend.txt | 68 ++++++ docs/output/86-unsafe-and-jni.txt | 49 +++++ docs/output/87-pinning.txt | 25 +++ docs/output/88-zgc-generational.txt | 26 +++ docs/output/89-joiner-api.txt | 24 +++ docs/output/90-unnamed-and-compact.txt | 26 +++ docs/output/91-final-field-mutation-27.txt | 11 + docs/output/92-maven-release.txt | 40 ++++ docs/output/93-gradle-toolchain.txt | 12 ++ docs/output/94-release-flag.txt | 24 +++ docs/output/95-lombok-matrix.txt | 19 ++ docs/output/96-jdeps-internals.txt | 15 ++ docs/output/97-mockito-bytebuddy.txt | 17 ++ g1-container/src/GcReport.java | 31 +++ g1-container/src/Workload.java | 67 ++++++ jep-tour/src/Check.java | 15 ++ jep-tour/src/ClientHelloSize.java | 45 ++++ jep-tour/src/Idle.java | 6 + jep-tour/src/LazyDemo.java | 94 +++++++++ jep-tour/src/PemDemo.java | 98 +++++++++ jep-tour/src/PrimitivePatterns.java | 95 +++++++++ jep-tour/src/StructuredDemo.java | 102 +++++++++ jep-tour/src/TlsPeer.java | 44 ++++ jep-tour/src/VectorDemo.java | 53 +++++ jep-tour/src/broken/Dominated.java | 13 ++ jep-tour/src/broken/Lazy26.java | 17 ++ jep-tour/src/broken/Pem26Style.java | 26 +++ jep-tour/src/broken/StructuredScope26.java | 23 ++ lanes/prompts/features-1-records.txt | 1 + lanes/prompts/features-2-pattern-switch.txt | 1 + lanes/prompts/features-3-virtual-threads.txt | 1 + .../features-4-sequenced-collections.txt | 1 + lanes/prompts/features-5-scoped-values.txt | 1 + .../features-6-structured-concurrency.txt | 1 + lanes/prompts/upgrade-1-pom.txt | 1 + lanes/prompts/upgrade-2-breaking-changes.txt | 1 + lanes/prompts/upgrade-3-preview-flags.txt | 1 + lanes/prompts/upgrade-4-modernize-idioms.txt | 1 + .../upgrade-5-virtual-thread-audit.txt | 1 + lanes/prompts/upgrade-6-checklist-runner.txt | 1 + lanes/prompts/upgrade-7-25-to-27.txt | 1 + lanes/src/Check.java | 15 ++ lanes/src/CompactHello.java | 3 + lanes/src/FinalizerDefault.java | 27 +++ lanes/src/HubExamples.java | 101 +++++++++ lanes/src/JniWarning.java | 13 ++ lanes/src/LombokProbe.java | 13 ++ lanes/src/MockitoProbe.java | 15 ++ lanes/src/PinningDemo.java | 29 +++ lanes/src/ReleaseFlag.java | 10 + lanes/src/ScopedValueDemo.java | 28 +++ lanes/src/SecurityManagerGone.java | 17 ++ lanes/src/StructuredHub.java | 37 ++++ lanes/src/StructuredHubNew.java | 21 ++ lanes/src/ThreadStopRuntime.java | 24 +++ lanes/src/ThreadSuspendGone.java | 11 + lanes/src/UnnamedVariables.java | 21 ++ lanes/src/UnsafeWarning.java | 21 ++ lanes/upgrade/build.gradle.kts | 15 ++ lanes/upgrade/pom.xml | 17 ++ lanes/upgrade/settings.gradle.kts | 1 + lanes/upgrade/src/main/java/demo/App.java | 12 ++ object-headers/src/HeaderDemo.java | 74 +++++++ object-headers/src/RecordProbe.java | 14 ++ other-changes/src/Api27.java | 42 ++++ other-changes/src/Check.java | 15 ++ other-changes/src/Compat.java | 13 ++ other-changes/src/SpawnProbe.java | 7 + recap26/src/AotApp.java | 11 + recap26/src/Api26.java | 44 ++++ recap26/src/Check.java | 15 ++ recap26/src/FinalFieldMutation.java | 33 +++ recap26/src/Http3Fallback.java | 53 +++++ recap26/src/broken/AppletGone.java | 7 + recap26/src/broken/ThreadStopGone.java | 7 + scripts/api-diff.sh | 17 ++ scripts/broken-on-27.sh | 32 +++ scripts/env.sh | 16 ++ scripts/g1-cost.sh | 25 +++ scripts/g1-default.sh | 49 +++++ scripts/g1-tuning.sh | 28 +++ scripts/jep-tour.sh | 17 ++ scripts/jfr-redaction.sh | 34 +++ scripts/lanes.sh | 136 ++++++++++++ scripts/object-headers.sh | 40 ++++ scripts/other-changes.sh | 41 ++++ scripts/recap26.sh | 101 +++++++++ scripts/run-all.sh | 22 ++ scripts/tls-pq.sh | 54 +++++ scripts/upgrade.sh | 110 ++++++++++ 152 files changed, 5049 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 api-diff/apidiff.py create mode 100644 api-diff/dump_api.py create mode 100644 docs/01-what-shipped.md create mode 100644 docs/02-g1-default.md create mode 100644 docs/03-compact-headers.md create mode 100644 docs/04-post-quantum-tls.md create mode 100644 docs/05-primitive-patterns.md create mode 100644 docs/06-lazy-constants.md create mode 100644 docs/07-structured-concurrency.md create mode 100644 docs/08-pem-api.md create mode 100644 docs/09-jfr-redaction-and-vector.md create mode 100644 docs/10-other-27-changes.md create mode 100644 docs/11-recap-26.md create mode 100644 docs/12-lanes-25-to-29.md create mode 100644 docs/13-upgrade-checklist.md create mode 100644 docs/14-lanes-21-to-25.md create mode 100644 docs/15-build-files.md create mode 100644 docs/output/01-g1-default-1cpu-vs-2cpu.txt create mode 100644 docs/output/02-g1-default-memory.txt create mode 100644 docs/output/03-g1-default-optout.txt create mode 100644 docs/output/04-g1-cost-1cpu.txt create mode 100644 docs/output/05-g1-cost-1cpu-short.txt create mode 100644 docs/output/06-g1-tuning-1cpu.txt create mode 100644 docs/output/10-headers-jdk26.txt create mode 100644 docs/output/11-headers-jdk27.txt create mode 100644 docs/output/12-headers-jdk27-opt-out.txt create mode 100644 docs/output/13-jol-record-failure.txt create mode 100644 docs/output/14-jol-record-workaround.txt create mode 100644 docs/output/15-headers-summary.txt create mode 100644 docs/output/20-broken-dominated.txt create mode 100644 docs/output/20-broken-lazy26.txt create mode 100644 docs/output/20-broken-pem26style.txt create mode 100644 docs/output/20-broken-structuredscope26.txt create mode 100644 docs/output/21-structured.txt create mode 100644 docs/output/22-lazy.txt create mode 100644 docs/output/23-primitive-patterns.txt create mode 100644 docs/output/24-pem.txt create mode 100644 docs/output/25-vector.txt create mode 100644 docs/output/30-tls-groups-matrix.txt create mode 100644 docs/output/31-tls-clienthello-size.txt create mode 100644 docs/output/32-tls-opt-out.txt create mode 100644 docs/output/40-jfr-redaction.txt create mode 100644 docs/output/50-vector-api-surface.txt create mode 100644 docs/output/51-api-diff-26-to-27.txt create mode 100644 docs/output/52-api-diff-25-to-26.txt create mode 100644 docs/output/60-removed-options.txt create mode 100644 docs/output/61-behaviour-changes.txt create mode 100644 docs/output/62-new-api.txt create mode 100644 docs/output/70-final-field-mutation.txt create mode 100644 docs/output/71-aot-cache-matrix.txt create mode 100644 docs/output/72-aot-startup.txt create mode 100644 docs/output/73-removed-in-26.txt create mode 100644 docs/output/74-api26.txt create mode 100644 docs/output/75-http3-fallback.txt create mode 100644 docs/output/80-hub-examples.txt create mode 100644 docs/output/81-scoped-values.txt create mode 100644 docs/output/82-structured-not-final.txt create mode 100644 docs/output/83-finalization.txt create mode 100644 docs/output/84-security-manager.txt create mode 100644 docs/output/85-thread-stop-suspend.txt create mode 100644 docs/output/86-unsafe-and-jni.txt create mode 100644 docs/output/87-pinning.txt create mode 100644 docs/output/88-zgc-generational.txt create mode 100644 docs/output/89-joiner-api.txt create mode 100644 docs/output/90-unnamed-and-compact.txt create mode 100644 docs/output/91-final-field-mutation-27.txt create mode 100644 docs/output/92-maven-release.txt create mode 100644 docs/output/93-gradle-toolchain.txt create mode 100644 docs/output/94-release-flag.txt create mode 100644 docs/output/95-lombok-matrix.txt create mode 100644 docs/output/96-jdeps-internals.txt create mode 100644 docs/output/97-mockito-bytebuddy.txt create mode 100644 g1-container/src/GcReport.java create mode 100644 g1-container/src/Workload.java create mode 100644 jep-tour/src/Check.java create mode 100644 jep-tour/src/ClientHelloSize.java create mode 100644 jep-tour/src/Idle.java create mode 100644 jep-tour/src/LazyDemo.java create mode 100644 jep-tour/src/PemDemo.java create mode 100644 jep-tour/src/PrimitivePatterns.java create mode 100644 jep-tour/src/StructuredDemo.java create mode 100644 jep-tour/src/TlsPeer.java create mode 100644 jep-tour/src/VectorDemo.java create mode 100644 jep-tour/src/broken/Dominated.java create mode 100644 jep-tour/src/broken/Lazy26.java create mode 100644 jep-tour/src/broken/Pem26Style.java create mode 100644 jep-tour/src/broken/StructuredScope26.java create mode 100644 lanes/prompts/features-1-records.txt create mode 100644 lanes/prompts/features-2-pattern-switch.txt create mode 100644 lanes/prompts/features-3-virtual-threads.txt create mode 100644 lanes/prompts/features-4-sequenced-collections.txt create mode 100644 lanes/prompts/features-5-scoped-values.txt create mode 100644 lanes/prompts/features-6-structured-concurrency.txt create mode 100644 lanes/prompts/upgrade-1-pom.txt create mode 100644 lanes/prompts/upgrade-2-breaking-changes.txt create mode 100644 lanes/prompts/upgrade-3-preview-flags.txt create mode 100644 lanes/prompts/upgrade-4-modernize-idioms.txt create mode 100644 lanes/prompts/upgrade-5-virtual-thread-audit.txt create mode 100644 lanes/prompts/upgrade-6-checklist-runner.txt create mode 100644 lanes/prompts/upgrade-7-25-to-27.txt create mode 100644 lanes/src/Check.java create mode 100644 lanes/src/CompactHello.java create mode 100644 lanes/src/FinalizerDefault.java create mode 100644 lanes/src/HubExamples.java create mode 100644 lanes/src/JniWarning.java create mode 100644 lanes/src/LombokProbe.java create mode 100644 lanes/src/MockitoProbe.java create mode 100644 lanes/src/PinningDemo.java create mode 100644 lanes/src/ReleaseFlag.java create mode 100644 lanes/src/ScopedValueDemo.java create mode 100644 lanes/src/SecurityManagerGone.java create mode 100644 lanes/src/StructuredHub.java create mode 100644 lanes/src/StructuredHubNew.java create mode 100644 lanes/src/ThreadStopRuntime.java create mode 100644 lanes/src/ThreadSuspendGone.java create mode 100644 lanes/src/UnnamedVariables.java create mode 100644 lanes/src/UnsafeWarning.java create mode 100644 lanes/upgrade/build.gradle.kts create mode 100644 lanes/upgrade/pom.xml create mode 100644 lanes/upgrade/settings.gradle.kts create mode 100644 lanes/upgrade/src/main/java/demo/App.java create mode 100644 object-headers/src/HeaderDemo.java create mode 100644 object-headers/src/RecordProbe.java create mode 100644 other-changes/src/Api27.java create mode 100644 other-changes/src/Check.java create mode 100644 other-changes/src/Compat.java create mode 100644 other-changes/src/SpawnProbe.java create mode 100644 recap26/src/AotApp.java create mode 100644 recap26/src/Api26.java create mode 100644 recap26/src/Check.java create mode 100644 recap26/src/FinalFieldMutation.java create mode 100644 recap26/src/Http3Fallback.java create mode 100644 recap26/src/broken/AppletGone.java create mode 100644 recap26/src/broken/ThreadStopGone.java create mode 100755 scripts/api-diff.sh create mode 100755 scripts/broken-on-27.sh create mode 100755 scripts/env.sh create mode 100755 scripts/g1-cost.sh create mode 100755 scripts/g1-default.sh create mode 100755 scripts/g1-tuning.sh create mode 100755 scripts/jep-tour.sh create mode 100755 scripts/jfr-redaction.sh create mode 100755 scripts/lanes.sh create mode 100755 scripts/object-headers.sh create mode 100755 scripts/other-changes.sh create mode 100755 scripts/recap26.sh create mode 100755 scripts/run-all.sh create mode 100755 scripts/tls-pq.sh create mode 100755 scripts/upgrade.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4d53cd4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/ +*.class +*.jfr +.lib/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..aa5473f --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4d881dc --- /dev/null +++ b/README.md @@ -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 +(). 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`. diff --git a/api-diff/apidiff.py b/api-diff/apidiff.py new file mode 100644 index 0000000..2a5b7d6 --- /dev/null +++ b/api-diff/apidiff.py @@ -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 [--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() diff --git a/api-diff/dump_api.py b/api-diff/dump_api.py new file mode 100644 index 0000000..d92b576 --- /dev/null +++ b/api-diff/dump_api.py @@ -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 """ +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') diff --git a/docs/01-what-shipped.md b/docs/01-what-shipped.md new file mode 100644 index 0000000..9f30251 --- /dev/null +++ b/docs/01-what-shipped.md @@ -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) diff --git a/docs/02-g1-default.md b/docs/02-g1-default.md new file mode 100644 index 0000000..95ccc87 --- /dev/null +++ b/docs/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) diff --git a/docs/03-compact-headers.md b/docs/03-compact-headers.md new file mode 100644 index 0000000..9e6ca3e --- /dev/null +++ b/docs/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) diff --git a/docs/04-post-quantum-tls.md b/docs/04-post-quantum-tls.md new file mode 100644 index 0000000..e4e34c8 --- /dev/null +++ b/docs/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) diff --git a/docs/05-primitive-patterns.md b/docs/05-primitive-patterns.md new file mode 100644 index 0000000..23e466e --- /dev/null +++ b/docs/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) diff --git a/docs/06-lazy-constants.md b/docs/06-lazy-constants.md new file mode 100644 index 0000000..39790fd --- /dev/null +++ b/docs/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 = 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) diff --git a/docs/07-structured-concurrency.md b/docs/07-structured-concurrency.md new file mode 100644 index 0000000..875f1ce --- /dev/null +++ b/docs/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.allSuccessfulOrThrow())) { + scope.fork(StructuredDemo::fetchPrice); + scope.fork(StructuredDemo::fetchStock); + List 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.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) diff --git a/docs/08-pem-api.md b/docs/08-pem-api.md new file mode 100644 index 0000000..9a60dc8 --- /dev/null +++ b/docs/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) diff --git a/docs/09-jfr-redaction-and-vector.md b/docs/09-jfr-redaction-and-vector.md new file mode 100644 index 0000000..5172d85 --- /dev/null +++ b/docs/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: . + +Prev: [8. PEM](08-pem-api.md) · Next: [10. Other 27 changes](10-other-27-changes.md) diff --git a/docs/10-other-27-changes.md b/docs/10-other-27-changes.md new file mode 100644 index 0000000..0a5eda2 --- /dev/null +++ b/docs/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