1.3 Lts Vs Non Lts Migration

Release Cadence Overview

Since Java 9, Oracle releases a new feature version every six months:

  • LTS (Long-Term Support) releases occur every 3 years (Java 8, 11, 17, 21, 25, etc.)
  • Non-LTS releases between LTS versions receive 6 months of support
  • Java 25 is a non-LTS release supported until September 2025

LTS releases receive updates for 5+ years, making them suitable for long-lived production systems.

When to Upgrade to Java 25

Good Reasons to Upgrade
  1. You're on Java 21 (LTS) and want latest features within the same support window
  2. You need Virtual Threads for high-concurrency workloads and cannot wait for Java 27 (next LTS)
  3. You're on legacy Java (8, 11, 17) and want modern API patterns and performance improvements
  4. Your dependencies require Java 25 (increasingly common as the ecosystem evolves)
Reasons to Wait for Java 27 (2025-09, next LTS)
  1. You run conservative long-lived systems requiring stable support for 5+ years
  2. Your team lacks CI/testing infrastructure for frequent JDK upgrades
  3. Your dependencies are not yet verified on Java 25
  4. You need preview features to stabilize before committing

Practical Upgrade Path

Step 1: Assess Current State
# Check your current Java version
java -version

# List all JDK installations
/usr/libexec/java_home -V  # macOS
java -XshowSettings:properties -version 2>&1 | grep java.home  # All platforms
Step 2: Test on Java 25 in CI
# Run your entire test suite on Java 25
java -version  # Verify JDK 25 is active
mvn clean test          # Maven
gradle test             # Gradle

# Capture deprecation warnings
javac -Xlint:deprecation src/main/java/**/*.java
Step 3: Fix Deprecations and Removals

Common migrations from Java 21 → 25:

Replace HttpURLConnection:

// Old (Java 8+, deprecated):
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("GET");

// Modern (Java 11+):
HttpClient client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder().GET().uri(URI.create(url)).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());

Replace java.util.Date with java.time:

// Old:
Date now = new Date();
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, 1);

// Modern:
LocalDateTime now = LocalDateTime.now();
LocalDateTime tomorrow = now.plusDays(1);

Replace POJOs with Records:

// Old:
public class User {
    private final String id, name;
    public User(String id, String name) { this.id = id; this.name = name; }
    public String getId() { return id; }
    public String getName() { return name; }
    @Override public boolean equals(Object o) { /* ... */ }
    @Override public int hashCode() { /* ... */ }
}

// Modern (Java 16+):
public record User(String id, String name) {}
Step 4: Enable Preview Features Safely

If your application uses preview features, compile and test with --enable-preview:

javac --enable-preview --release 25 src/main/java/**/*.java
java --enable-preview -cp target/classes MyApplication

But do not use preview features in production unless you plan to upgrade within 6 months.

Step 5: Performance Testing

Run realistic load tests to compare Java 21 and Java 25:

# With Java 21
java -Xmx4g -XX:+UseG1GC MyApplication &
# Run load tests, capture metrics

# With Java 25
java -Xmx4g -XX:+UseG1GC MyApplication &
# Run same load tests, compare

Compare:

  • Throughput (requests/second)
  • Latency (p50, p99, p99.9)
  • Memory usage and GC pause times
  • CPU utilization
Step 6: Canary Deployment
  1. Deploy to a small percentage of production traffic (5-10%)
  2. Monitor error rates, latency, and memory for 24+ hours
  3. If stable, increase traffic gradually
  4. Maintain rapid rollback capability
# Example: Route 10% of traffic to Java 25 instances
# (Kubernetes, load balancer, or feature flags)

Decision Matrix

Situation Recommendation Rationale
On Java 21 LTS, stable codebase Stay on 21, upgrade to 27 Security patches sufficient; 27 (next LTS) in 2025-09
On Java 21, need Virtual Threads now Upgrade to 25 VT maturity justifies interim non-LTS; plan for 27 upgrade
On Java 11/17, fast-moving services Jump to 25 Multiple feature releases and optimizations since 11/17
On Java 8, legacy monolith Plan for 21 LTS 21 offers stable, long-support baseline; 25 adds little over 21
Early adopter, microservices Go to 25, plan for 27 Experiment with latest features; upgrade every 6mo

Long-Term Support Strategy

Conservative Approach (Enterprise):

  • Baseline on LTS versions (21, 27, 33, ...)
  • Upgrade every 3 years on LTS boundaries
  • Use 5+ years of security patch support per LTS

Agile Approach (Fast-Moving Services):

  • Baseline on current feature release (25 today, 26 in March, 27 in September)
  • Upgrade every 6 months with minor code changes
  • Automate CI/CD for rapid testing and deployment

Hybrid Approach:

  • Core infrastructure on LTS (21)
  • Microservices on latest feature release (25)
  • Coordinate upgrades quarterly

Troubleshooting Common Issues

"Module not found" after upgrade

Internal JDK APIs were removed. Use jdeps to identify dependencies:

jdeps -jdkinternals myapp.jar

Migrate to public APIs or FFM for native code.

Performance regression on Java 25

Enable detailed GC logging to diagnose:

java -Xlog:gc*:file=gc.log MyApplication
# Analyze gc.log; compare GC tuning between versions
Dependency incompatibilities

Check if your frameworks/libraries are certified for Java 25:

  • Spring Framework 6.1+
  • Hibernate 6.4+
  • Quarkus 3.5+ Update dependencies before upgrading the JDK.

Summary

  • Java 25 is a non-LTS feature release; excellent for adopting new capabilities
  • Upgrade if you need virtual threads, latest API features, or modern performance improvements
  • Wait for Java 27 LTS if you prioritize long-term stability and support
  • Test thoroughly on Java 25 in CI before production deployment
  • Plan for Java 27 as your next LTS baseline by September 2025