Repository navigation
Releases
v0.1.4
Faster thread hand-offs, less memory for large programs, and a smaller download for programs started from a jar.
- A program started from a jar no longer fetches
jdk/app.pack:boot.packnow covers what such a start reads, so a cold start from a jar fetches 2.42 MB instead of 3.65 MB gzipped (#84). - A thread woken by another now runs within the waker's scheduler turn. Park/unpark and wait/notify hand-offs are about twice as fast, a hand-off next to busy threads went from 82 s to 4 s, and a parked virtual thread takes 3.7 KB instead of 5.6 KB (#87).
- The JIT emits 13% less JavaScript and keeps less per method: heap after compiling 30 files with javac went from about 167 MB to 146 MB (#88).
AtomicIntegerupdates run about 3 times as fast (#88), and MD5 about 13 times (#90). - Closing
System.outorSystem.errcloses the host's stream, so a reader sees end of file (#82). Under Node, a listening socket honors its backlog as on Linux (#89). - The set of files in
jdk/is unchanged.
Performance
- crypto: run MD5 implCompress0 as a native, as HotSpot intrinsifies it (#90)
- jit: emit 13% less JS and keep less per method, cutting javac's heap by 13% (#88)
- sched: run a woken thread within the waker's turn so thread hand-offs do not wait a scheduling round (#87)
- build: record boot.pack from a start from a jar too, so it needs no app.pack (#84)
Fixes
- net: hold a connect to a full accept queue until accept makes room, as Linux does (#89)
- io: close the host stdout or stderr when Java closes descriptor 1 or 2 (#82)
Build and CI
- jtreg: run each shard's tests slowest first so a long test does not start last (#86)
- jtreg: run only the OpenJDK tests that cover java-web code no faster test covers (#79)
Documentation
- state in AGENTS.md that java-web targets Java in general (#81)
- compare java-web with CheerpJ 5.0 on Java 25 in the README (#80)
Other
- jtreg: run seven more Collator tests with locale data (#85)
- build: name the javac.pack span intersection with an intersect helper (#83)
- add a release notes script and the release steps to AGENTS.md (#78)
Full list: v0.1.3...v0.1.4
v0.1.3
Smaller downloads, and a JDK image a site can trim.
- The JDK image is no longer one 125 MB
jdk/image.jar.jdk/image.jar(31 MB) holdsjava.baseand the files around the classes, each other module has its ownjdk/<module>.jar, andjdk/ct.sym.jarholds whatjavac --releasereads. Every file is under 30 MB. A site can leave out the files of modules its pages never load; a class in a left-out module throwsNoClassDefFoundErrorand the console names the missing file (#75). - A new
jdk/app.pack, fetched the first time a program needs it, holds what typical programs read beyond a bare start, andjdk/javac.packis now recorded from a realistic compile. A cold session fetches far less: a compile of a multi-file project with annotation processors went from 16.5 MB to 8.2 MB, and starting a program from its jar from 9.1 MB to 5.4 MB (#76). - Serve
jdk/app.packalong with the rest ofjdk/.
Features
- put each JDK module but java.base in its own image file (#75)
Performance
- add app.pack and record javac.pack from a realistic compile (#76)
Documentation
- GitHub Pages must deploy from Actions to serve jdk/image.jar (#74)
Full list: v0.1.2...v0.1.3
v0.1.2
Faster starts, a real image-mode JDK, warm library VMs, child processes, file watching, large files, less memory per VM, and compatibility fixes.
Upgrading
- The JDK now ships as one file,
jdk/image.jar, in place ofjdk/<module>.jarandjdk/jdk.localedata.txt. A page that servesdist/needs no change, but the server must still answer Range requests, now forjdk/image.jar. - The
java-webcommand now runsdist/cli.js.dist/node.jsand its exports are unchanged. - Node keeps boot snapshots in
~/.cache/java-web/snapshots(up to 16). SetJAVA_WEB_SNAPSHOT_DIRto move it, or to an empty value to turn snapshots off.
New
- Child processes under Node:
ProcessBuilder,Runtime.execandProcessHandlestart and manage the host's programs, with pipes, pipelines, redirects, environment and working directories as on HotSpot. Startingjavastarts another java-web VM on the same image. (#45) WatchServicereports files created, changed and deleted inside the VM. (#46)- Files of any size: a file streams to the host once it passes 4 MiB instead of living in memory, so multi-GB files work under Node. A 256 MiB write and read went from 829 MB and 36 s to 338 MB and 2.9 s. (#47)
- Warm snapshots, opt-in with
warmSnapshot: true(startLibraryunder Node,cheerpjInitin the browser): a library VM is saved again once its calls have warmed it up, so a later start skips javac's cold start. A first javac compile takes about a fifth of the time under Node and half in Chromium. The image also keeps the JIT's generated code, which makes the first call after a restore about 20% faster still. (#48, #58, #59) cheerpjInit({ maxHeap: "256m" })caps the whole Java heap in the browser, as-Xmxdoes under Node: a program that fills it getsOutOfMemoryError: Java heap spaceinstead of the tab dying. (#57)cheerpjInit({ debug: true })logs VM events to the console, such as whether a warm snapshot was saved or restored. (#52)jdk.managementis in the image:com.sun.management.UnixOperatingSystemMXBean(open and maximum descriptor counts) andHotSpotDiagnosticMXBean.getVMOption. (#61)- Symbolic and hard links on host file systems,
SecureDirectoryStream, and new files and directories created with the requested mode less the umask. (#61) Deflatercompresses: zlib's levels, strategies, flush modes and dictionaries. Level 6 output is within 0.1% of zlib's size, and a jar of 1,439 classes comes out 54% smaller. (#63)ThreadMXBeanthread info, thread dumps with locked monitors and synchronizers, and deadlock detection for monitors andjava.util.concurrentlocks, with no cost tosynchronizedorReentrantLock. (#65)- Virtual threads no longer pin their carrier when they block on a monitor or wait in
Object.wait, as in JDK 24 and later. (#68) - Launcher options:
-mand--module,-Xbootclasspath/a(directories and jars, repeatable) and--patch-modulefor boot modules.bin/javaexists in the image, andkill -QUITprints a thread dump. (#69) jdk.netandjdk.nio.mapmodeare in the image. ATZvalue that is a POSIX rule, such asMEZ-1MESZ,M3.5.0,M10.5.0/3, sets the default time zone's offset and daylight saving dates. Memory pools and managers have G1's names, as HotSpot's default collector reports them. (#69)
Faster
- Boot snapshots: a VM starts from the state saved after an earlier boot with the same inputs. Hello world under Node goes from about 2.3 s to under 0.5 s. In the browser, later runs on a page start in about 0.3 s. (#29)
- Image-mode JDK: the JDK is a jlinked
lib/modules, as a normal JDK has, instead of an exploded tree. A full boot under Node drops from 1.9 s to 1.3 s, andjrt:/no longer walks every file of the image. (#37) - javac's first compile is about 40% faster in Node (10.3 s to 6.0 s from a snapshot) and in the browser (6.2 s to 3.7 s on a cold page). (#34, #37)
- Browser downloads: one request per app class instead of two, a smaller boot index, and the image ranges a start and javac read packed into
boot.packandjdk/javac.pack. (#30, #31, #37) - Node's compile cache for the
java-webcommand, cheaper calls between compiled and interpreted code, and intrinsics for SHA, AES, BigInteger,Math.multiplyHighand P-256 (EC work about 44% faster). (#20, #21, #26, #27, #28, #33) - Less heap per VM: UTF-8 constants are plain strings, parsed member records are dropped after linking, and the constant pool lives in typed arrays. Each saves a few MB; the constant pool change alone takes about 8 MB off a library VM after a javac compile. (#41, #55, #60)
- Unmapping a file gives its memory back. (#56)
- Zip reading and writing: faster inflate and CRC32, native array compare and hash code (which also speeds up
Arrays.equals,Arrays.mismatchandString.hashCode), and calls that see one receiver class are inlined. A 12 MB deflated zipfs entry writes and verifies in 449 ms instead of 1,154 ms. (#62) - Uncontended
synchronizedis 12 to 38% faster, and contended monitors between platform threads about 30% faster. (#68) - Startup and memory: class file parts are decoded on first use, line tables point into the class bytes, and generated code is smaller. About 21% less heap after a javac compile (101 to 80 MB), and hello world from a boot snapshot about 13% faster. Snapshots from older builds are rebuilt once. (#70)
Fixes
- Class initialization, exception handlers and bytecode edge cases now match HotSpot. (#22, #25)
- Files: writes the host refuses are reported, a new file the host refuses reports the host's reason, renaming a file over a directory is refused, and special characters in browser file paths are kept. (#19, #23, #24, #35)
- Modification times set to the nanosecond read back exactly. (#38)
FileStoreandFile.getTotalSpace(),getFreeSpace()andgetUsableSpace()report the host file system's figures, and direct I/O (ExtendedOpenOption.DIRECT) works. (#42)java.util.prefsstores preferences: the file natives behindPreferences.userRoot()are implemented. (#43)- A process that upgrades the package in place keeps each running VM on the image it started with. (#39)
- The
java-webcommand no longer hangs at exit on some x64 hosts after a program used several hundred MB. (#44) - A
RandomAccessFileopened read-only refuses writes, as on HotSpot. (#47) - A thread that joins the main thread no longer deadlocks when main ends, for example with an uncaught exception. (#49)
- Under Node,
-Xmxcaps the whole heap: a program that fills it getsOutOfMemoryError: Java heap space, which it can catch, instead of being killed. Before, it only capped a single array. (#50, #53) - Writing far past the end of a file, as sparse files do, no longer allocates the gap, so multi-GB files work. Append mode follows
O_APPEND, and a directory can be opened read-only as a channel. (#54) - A file mapping that does not fit fails with
IOException: Map failed, as on HotSpot, instead of crashing. (#56) - A warm or boot snapshot of a deep object graph (a long linked list, for example) no longer overflows the stack while saving or restoring. Older snapshot files are ignored and rebuilt. (#59)
- An open file keeps its inode number after its path is deleted, child programs are found on the parent's
PATH, and an unreadable file fails withEACCES. (#61) jrt:/reads class files, and stack frames carry module versions as on HotSpot. (#37)Runtimereports real heap figures. (#26)- A VM that hits an internal error on its main thread exits with status 1 instead of hanging. (#29)
park,Object.waitandThread.sleepwith a timeout longer than about 24 days, such asLockSupport.parkNanos(Long.MAX_VALUE), no longer wake after 1 ms and spin. (#66)-XX:OnOutOfMemoryErrorruns its commands and-XX:MaxDirectMemorySizelimits direct buffers, as on HotSpot.java -versionworks. (#67)- File access times are kept and reported: reading a host file no longer changes its access time, and setting times on a symlink with
NOFOLLOW_LINKSchanges only the link. (#67) WatchServicesees changes other processes make in a watched host directory, and no longer hangs when a watched directory is deleted during registration or crashes on a long event queue. (#67, #72)- Sockets: a socket reports writable after
shutdownOutput, a connected datagram socket can connect again, Unix domain sockets bind the unnamed address and long paths, and an accepted Unix socket reports its client's name. (#69) - Processes: a child's output is read in full before the VM hears it exited, merged output included; the
PATHsearch and exec errors follow the JDK's; an unlimited open file limit reports -1. (#69, #72) - A stack overflow inside an overflow handler throws a bare
StackOverflowErrorinstead of failing the VM. TheConstantPoolnatives check their index and entry kind. (#69) Deflater.setDictionaryin the middle of a raw stream no longer drops input it had taken but not yet written. (#72)- An open file shares bytes with another open of the same path only when they are the same file, so reopening a replaced symlink target reads the new file. (#72)
- An
OutOfMemoryErroris thrown even when the hook that reports it fails, and-XX:OnOutOfMemoryErrorcommands run even when their header cannot be written. (#72)
Tests
CI now runs about 4,000 of OpenJDK's own regression tests (up from 2,524 in 0.1.1), split into eight shards balanced by run time, from the built bundle, and retries rate-limited framework downloads. The test runner evaluates @requires as jtreg does, expands jtreg's variables in @run lines, puts @library directories on test.src.path, and supports @clean, @run driver and @run build. Tests that need other locales run with locale data. (#32, #36, #40, #42, #43, #44, #45, #46, #47, #49, #50, #51, #54, #56, #61, #65, #67, #68, #69, #71). Pull requests run the test directories their changes can affect plus ...
v0.1.1
Changes
- The
java-webcommand honors-Xmxas x64 HotSpot does: it caps one allocation and sets whatRuntime.maxMemory()and the memory MXBean report, rounds up to 2 MB steps, and refuses sizes under 2 MB or malformed values. (#13) - A closed stdout or stderr (
java-web Main | head -1) no longer crashes the command: writes fail withIOException: Broken pipeandSystem.out.checkError()turns true, as on HotSpot. (#10) javaWebWriteStdinis documented in docs/browser.md, and calling it beforecheerpjInitthrows a clear error. (#11)
Project
- Pull requests run the OpenJDK tests their changes need, in parallel jobs, and
masterrequires them. (#12, #14) @types/node26.6.6, and CI on Node 26. (#16, #17)
Full Changelog: v0.1.0...v0.1.1
v0.1.0
What's Changed
- Ship a Node entry in the npm package by @Project516 in #4
- Publish as @project516/java-web by @Project516 in #7
- Fix the release: File permissions, jtreg timeouts, public scoped package by @Project516 in #8
- Make System.gc collect under the java-web command by @Project516 in #9
New Contributors
- @Project516 made their first contribution in #4
Full Changelog: https://github.com/Project516/java-web/commits/v0.1.0