Java 11 Removed/Deprecated Features & Migration

1. Introduction

Java 11 was released in September 2018 as the first Long-Term Support (LTS) release after Java 8. This makes it one of the most important upgrade targets in Java history. If your organization skipped Java 9 and 10 (as most did), migrating from Java 8 to Java 11 means dealing with three major versions of changes at once — the module system (Java 9), API removals, deprecated features, and new defaults.

The good news: millions of projects have made this migration successfully. The bad news: it is not a simple recompile. Java 11 removed several APIs that were bundled with the JDK since the early days, and the module system changes how the classpath and class loading work. If you do not prepare, you will hit compilation errors and runtime exceptions that did not exist in Java 8.

This guide covers everything that was removed, deprecated, or changed, and provides the exact replacements and fixes you need. Here is a high-level overview:

Category What Changed Impact Level
Java EE Modules JAXB, JAX-WS, CORBA, javax.activation, javax.annotation removed High — breaks most enterprise apps
JavaFX Removed from JDK, now a separate project (OpenJFX) High for desktop apps
Nashorn JavaScript Deprecated (removed in Java 15) Medium — affects apps embedding JS
Module System JPMS (Java 9) affects classpath, reflection, internal APIs High — affects most large apps
Deprecated APIs Pack200, Applet API, SecurityManager Low-Medium
Build Tools Maven/Gradle plugins need updates Medium

Target audience: This guide is for teams migrating from Java 8 to Java 11. If you are on Java 9 or 10, many of these changes are already familiar, but the removal of Java EE modules (which were only deprecated in Java 9) is new in Java 11.

2. Removed Java EE Modules

This is the change that breaks the most Java 8 applications. Java has shipped with several Java EE APIs since Java 6, bundled as part of the JDK itself. In Java 9, these modules were deprecated. In Java 11, they were completely removed. If your code uses any of these APIs, it will fail with ClassNotFoundException or NoClassDefFoundError on Java 11.

javax.xml.bind (JAXB)

JAXB (Java Architecture for XML Binding) marshals Java objects to XML and back. It was incredibly common in SOAP web services and enterprise applications. If your code has import javax.xml.bind.*, it will break on Java 11.

Maven replacement:



    jakarta.xml.bind
    jakarta.xml.bind-api
    4.0.2


    org.glassfish.jaxb
    jaxb-runtime
    4.0.5
    runtime

Note on the Jakarta namespace: The Java EE APIs have been transferred to the Eclipse Foundation under the Jakarta EE umbrella. Newer versions use the jakarta.* package prefix instead of javax.*. If you need to maintain the old javax.xml.bind package names for compatibility with existing code, use the older 2.x versions:



    javax.xml.bind
    jaxb-api
    2.3.1


    org.glassfish.jaxb
    jaxb-runtime
    2.3.9
    runtime

Code example — JAXB works the same, just needs the dependency:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.StringWriter;

@XmlRootElement
public class Employee {
    private String name;
    private String department;

    // Default constructor required by JAXB
    public Employee() {}

    public Employee(String name, String department) {
        this.name = name;
        this.department = department;
    }

    // Getters and setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getDepartment() { return department; }
    public void setDepartment(String department) { this.department = department; }

    public static void main(String[] args) throws Exception {
        Employee emp = new Employee("Alice", "Engineering");

        JAXBContext context = JAXBContext.newInstance(Employee.class);
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        StringWriter writer = new StringWriter();
        marshaller.marshal(emp, writer);
        System.out.println(writer.toString());
    }
}

// Output (after adding JAXB dependency):
// 
// 
//     Engineering
//     Alice
// 

javax.xml.ws (JAX-WS)

JAX-WS was the API for SOAP web services. If your application generates SOAP clients from WSDL files or provides SOAP endpoints, you need this replacement.

Maven replacement:


    jakarta.xml.ws
    jakarta.xml.ws-api
    4.0.2


    com.sun.xml.ws
    jaxws-rt
    4.0.3
    runtime

javax.activation (JavaBeans Activation Framework)

The Activation Framework is used for MIME type handling and is a dependency of JAXB and JavaMail. Even if you do not use it directly, you may need it as a transitive dependency.

Maven replacement:


    jakarta.activation
    jakarta.activation-api
    2.1.3

javax.annotation

This module provided annotations like @PostConstruct, @PreDestroy, @Resource, and @Generated. These are heavily used in Spring, CDI, and other dependency injection frameworks.

Maven replacement:


    jakarta.annotation
    jakarta.annotation-api
    3.0.0




    javax.annotation
    javax.annotation-api
    1.3.2

CORBA

CORBA (Common Object Request Broker Architecture) was a 1990s-era technology for distributed computing. It was removed entirely from Java 11, and there is no standalone replacement because the technology is essentially dead. If you have legacy CORBA code, your options are limited to staying on Java 8 for that component or migrating to REST/gRPC.

Complete Removed Modules Summary

Removed Module Package What It Did Maven Replacement (groupId:artifactId)
java.xml.bind javax.xml.bind XML-to-Java object binding (JAXB) jakarta.xml.bind:jakarta.xml.bind-api + org.glassfish.jaxb:jaxb-runtime
java.xml.ws javax.xml.ws SOAP web services (JAX-WS) jakarta.xml.ws:jakarta.xml.ws-api + com.sun.xml.ws:jaxws-rt
java.xml.ws.annotation javax.annotation Common annotations (@PostConstruct, etc.) jakarta.annotation:jakarta.annotation-api
java.activation javax.activation MIME type handling jakarta.activation:jakarta.activation-api
java.corba org.omg.CORBA Distributed object computing None — technology is obsolete
java.transaction javax.transaction JTA (Java Transaction API) jakarta.transaction:jakarta.transaction-api

3. Removed JavaFX

JavaFX, the modern GUI toolkit that was meant to replace Swing, was removed from the JDK starting with Java 11. It now lives as an independent open-source project called OpenJFX.

If your application uses JavaFX, you need to add it as an explicit dependency:



    org.openjfx
    javafx-controls
    21.0.5


    org.openjfx
    javafx-fxml
    21.0.5




    org.openjfx
    javafx-maven-plugin
    0.0.8
    
        com.example.App
    

For Gradle:

// build.gradle
plugins {
    id 'org.openjfx.javafxplugin' version '0.1.0'
}

javafx {
    version = "21.0.5"
    modules = ['javafx.controls', 'javafx.fxml']
}

The OpenJFX project is actively maintained and continues to receive updates. Your existing JavaFX code should work with minimal changes — the main effort is adding the dependency and configuring the module path.

4. Removed Nashorn JavaScript Engine

The Nashorn JavaScript engine (javax.script with engine name “nashorn”) was deprecated in Java 11 (JEP 335) and fully removed in Java 15. Nashorn was introduced in Java 8 as a replacement for the even older Rhino engine, but maintaining a full JavaScript engine inside the JDK proved impractical given how quickly JavaScript evolves.

Alternatives:

Alternative Description Best For
GraalJS JavaScript engine from GraalVM project Drop-in replacement for Nashorn, modern ECMAScript support
J2V8 Java bindings for Google V8 engine Performance-critical JavaScript execution
Rhino Mozilla’s JavaScript engine (standalone) Legacy compatibility

If you are using Nashorn to evaluate JavaScript expressions or run scripts, GraalJS is the recommended replacement:



    org.graalvm.js
    js
    23.0.3


    org.graalvm.js
    js-scriptengine
    23.0.3
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class JavaScriptExample {
    public static void main(String[] args) throws Exception {
        // With GraalJS on the classpath, this uses GraalJS instead of Nashorn
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("graal.js");

        if (engine != null) {
            Object result = engine.eval("1 + 2 + 3");
            System.out.println("Result: " + result);  // Result: 6
        } else {
            System.out.println("GraalJS engine not found. Add graaljs dependency.");
        }
    }
}

5. Removed java.se.ee Aggregator Module

The java.se.ee module was an aggregator module that included all the Java EE modules listed above (JAXB, JAX-WS, CORBA, etc.). In Java 9, it was deprecated. In Java 11, it was removed along with all the modules it aggregated.

If your module-info.java contains requires java.se.ee;, remove it and add explicit dependencies for the specific modules you need (using the Maven dependencies shown in section 2).

6. Deprecated Features

These features were deprecated in Java 11 but not yet removed. They will be removed in future Java versions:

Pack200 Tools and API

Pack200 was a compression scheme for JAR files. It was deprecated in Java 11 (JEP 336) and removed in Java 14. The pack200 and unpack200 command-line tools and the java.util.jar.Pack200 API are gone.

Impact: If your build process or deployment pipeline uses Pack200 compression, switch to standard ZIP/JAR compression or use jlink to create custom runtime images (which are smaller than compressed JARs anyway).

Applet API

The Applet API (java.applet.Applet) was deprecated in Java 9 and carries a forRemoval=true annotation. Browsers dropped support for Java applets years ago. If you still have applet-based code, migrate to Java Web Start (also deprecated) or web-based alternatives.

SecurityManager

SecurityManager was not deprecated in Java 11 itself, but it was deprecated for removal in Java 17 (JEP 411). If you rely on SecurityManager for sandboxing, start planning your migration now. Java is moving toward other security mechanisms (process isolation, containers, etc.).

7. Module System Considerations

The Java Platform Module System (JPMS), introduced in Java 9, is the biggest architectural change in Java’s history. While you do not have to modularize your application to run on Java 11, the module system still affects classpath-based applications in important ways.

The Unnamed Module and the Classpath

If you do not create a module-info.java, your entire application runs in the unnamed module. The unnamed module can access all exported packages from all named modules, so most code works without changes. However:

// This worked in Java 8 but may not in Java 11:
// Accessing internal JDK APIs
import sun.misc.Unsafe;           // Encapsulated in java.base
import sun.misc.BASE64Encoder;     // Removed entirely

// Fix: Use the public API equivalents
import java.util.Base64;           // Replacement for BASE64Encoder/Decoder
// For Unsafe, use VarHandle (Java 9+) or MethodHandles

Common Module System Flags

If your application or its dependencies access internal JDK APIs, you may need these JVM flags:

Flag Purpose Example
--add-modules Add a module to the module graph --add-modules java.sql
--add-opens Open a package for deep reflection (setAccessible) --add-opens java.base/java.lang=ALL-UNNAMED
--add-exports Export a package to another module --add-exports java.base/sun.nio.ch=ALL-UNNAMED
--add-reads Add a read edge between modules --add-reads mymodule=java.logging
--illegal-access Control illegal reflective access (removed in Java 17) --illegal-access=permit (Java 11-16 only)

Example: Running a Framework That Uses Reflection

Many frameworks (Spring, Hibernate, Jackson) use deep reflection to access private fields. On Java 11, you may see warnings like:

// Warning you may see at runtime:
// WARNING: An illegal reflective access operation has occurred
// WARNING: Illegal reflective access by org.springframework.core.io.support...
// WARNING: Please consider reporting this to the maintainers of org.springframework...

// Fix: Add --add-opens flags to your JVM startup
// For Spring Boot applications, add to your startup script or JAVA_OPTS:
// java --add-opens java.base/java.lang=ALL-UNNAMED \
//      --add-opens java.base/java.lang.reflect=ALL-UNNAMED \
//      --add-opens java.base/java.util=ALL-UNNAMED \
//      -jar myapp.jar

// For Maven Surefire plugin (test execution):
// 
//     org.apache.maven.plugins
//     maven-surefire-plugin
//     3.2.5
//     
//         
//             --add-opens java.base/java.lang=ALL-UNNAMED
//             --add-opens java.base/java.util=ALL-UNNAMED
//         
//     
// 

Best practice: Update your frameworks and libraries to versions that support Java 11 natively. Modern versions of Spring (5.1+), Hibernate (5.4+), and Jackson (2.10+) have been updated to avoid illegal reflective access.

8. Migration Checklist

Follow this step-by-step checklist when migrating from Java 8 to Java 11:

Phase 1: Preparation

Step Action Details
1 Inventory your dependencies List all Maven/Gradle dependencies and check their Java 11 compatibility. Use mvn dependency:tree to see the full tree.
2 Check for Java EE API usage Search your codebase for import javax.xml.bind, import javax.xml.ws, import javax.annotation. These will break.
3 Check for internal API usage Search for import sun., import com.sun.. These are encapsulated in Java 11.
4 Update build tools Maven 3.5.0+ and Gradle 5.0+ are minimum for Java 11 support. See section 10 for plugin versions.
5 Create a branch Work on a dedicated branch. Keep the Java 8 version running in production until migration is validated.

Phase 2: Compile and Fix

Step Action Details
6 Set compiler target to 11 Update maven-compiler-plugin source and target to 11. See section 10.
7 Add Java EE replacement dependencies Add JAXB, JAX-WS, javax.annotation dependencies from section 2.
8 Fix compilation errors Address removed API usage, internal API access, and deprecated method warnings.
9 Run the test suite Fix any test failures. Pay attention to reflection-based tests and XML processing.

Phase 3: Runtime Validation

Step Action Details
10 Test with Java 11 JVM Run the application and check for IllegalAccessError, ClassNotFoundException, or reflective access warnings.
11 Add --add-opens flags if needed For frameworks that use deep reflection, add the necessary module opens.
12 Test critical paths Test XML processing, serialization, database access, and any integration points.
13 Performance test Java 11 has improved garbage collectors (G1 is now default). Run performance benchmarks.
14 Deploy to staging Run in a production-like environment for at least a week before production deployment.

9. Common Migration Issues and Fixes

This table covers the most frequently encountered issues when migrating from Java 8 to Java 11:

Issue Error Message Fix
JAXB missing ClassNotFoundException: javax.xml.bind.JAXBContext Add jakarta.xml.bind:jakarta.xml.bind-api + org.glassfish.jaxb:jaxb-runtime
JAX-WS missing ClassNotFoundException: javax.xml.ws.Service Add jakarta.xml.ws:jakarta.xml.ws-api + com.sun.xml.ws:jaxws-rt
@PostConstruct missing ClassNotFoundException: javax.annotation.PostConstruct Add jakarta.annotation:jakarta.annotation-api
sun.misc.BASE64 removed ClassNotFoundException: sun.misc.BASE64Encoder Replace with java.util.Base64.getEncoder()
sun.misc.Unsafe IllegalAccessError Use --add-opens java.base/jdk.internal.misc=ALL-UNNAMED or migrate to VarHandle
Reflective access warning WARNING: An illegal reflective access operation has occurred Update the library, or add --add-opens for the specific package
JavaFX missing ClassNotFoundException: javafx.application.Application Add OpenJFX dependencies (see section 3)
Nashorn missing ScriptEngine is null Add GraalJS dependency (see section 4)
Lombok compilation error Various annotation processor errors Update Lombok to 1.18.4+ (supports Java 11)
JaCoCo code coverage fails IllegalArgumentException: Unsupported class file major version Update JaCoCo to 0.8.3+
Mockito reflection error InaccessibleObjectException Update Mockito to 2.23+ or add --add-opens
Spring Boot startup error Various CGLIB/ASM errors Update to Spring Boot 2.1+ (first version with full Java 11 support)

Replacing sun.misc.BASE64Encoder

This is one of the most common fixes. The old internal API was never part of the public specification:

import java.util.Base64;

public class Base64Migration {
    public static void main(String[] args) {
        String original = "Hello, Java 11!";

        // --- BEFORE: Java 8 internal API (REMOVED in Java 11) ---
        // sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
        // String encoded = encoder.encode(original.getBytes());
        // sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
        // byte[] decoded = decoder.decodeBuffer(encoded);

        // --- AFTER: Standard API (Java 8+) ---
        // Encode
        String encoded = Base64.getEncoder().encodeToString(original.getBytes());
        System.out.println("Encoded: " + encoded);

        // Decode
        byte[] decodedBytes = Base64.getDecoder().decode(encoded);
        String decoded = new String(decodedBytes);
        System.out.println("Decoded: " + decoded);

        // URL-safe encoding (for URLs and filenames)
        String urlEncoded = Base64.getUrlEncoder().encodeToString(original.getBytes());
        System.out.println("URL-safe: " + urlEncoded);

        // MIME encoding (with line breaks every 76 chars)
        String mimeEncoded = Base64.getMimeEncoder().encodeToString(
                "A longer string that would benefit from MIME line wrapping".getBytes());
        System.out.println("MIME: " + mimeEncoded);
    }
}

// Output:
// Encoded: SGVsbG8sIEphdmEgMTEh
// Decoded: Hello, Java 11!
// URL-safe: SGVsbG8sIEphdmEgMTEh
// MIME: QSBsb25nZXIgc3RyaW5nIHRoYXQgd291bGQgYmVuZWZpdCBmcm9tIE1JTUUgbGluZSB3cmFw
// cGluZw==

Finding Internal API Usage

Java provides the jdeps tool to scan your JARs for internal API usage. Run this before migrating to identify problems early:

// Command: scan a JAR for internal API dependencies
// jdeps --jdk-internals myapp.jar
//
// Example output:
// myapp.jar -> java.base
//    com.myapp.util.Encoder -> sun.misc.BASE64Encoder  JDK internal API (removed)
//    com.myapp.security.Crypto -> sun.security.ssl.SSLSessionImpl  JDK internal API
//
// Command: check module dependencies
// jdeps --module-path libs --print-module-deps myapp.jar
//
// Example output:
// java.base,java.logging,java.sql,java.xml

10. Build Tool Updates

Your build tools and their plugins must be updated to support Java 11. Here are the minimum versions required:

Maven

Component Minimum Version Recommended Version Notes
Maven itself 3.5.0 3.9.6+ Older versions may not recognize Java 11 bytecode
maven-compiler-plugin 3.8.0 3.12.1+ Required for release flag support
maven-surefire-plugin 2.22.0 3.2.5+ Needed for Java 11 test execution
maven-failsafe-plugin 2.22.0 3.2.5+ Integration test plugin
maven-jar-plugin 3.1.0 3.3.0+ Correct manifest generation
jacoco-maven-plugin 0.8.3 0.8.11+ Code coverage for Java 11 bytecode

Maven compiler configuration for Java 11:


    11
    11
    11



    
        
        
            org.apache.maven.plugins
            maven-compiler-plugin
            3.12.1
            
                11
            
        

        
        
            org.apache.maven.plugins
            maven-surefire-plugin
            3.2.5
            
                
                
                    --add-opens java.base/java.lang=ALL-UNNAMED
                    --add-opens java.base/java.util=ALL-UNNAMED
                
            
        
    

Why use release instead of source/target? The --release flag (Java 9+) is a single setting that simultaneously sets the source level, target level, and restricts the available APIs to those present in the specified Java version. Using source and target alone lets you accidentally use Java 17 APIs even when targeting Java 11, causing runtime errors on Java 11.

Gradle

Component Minimum Version Recommended Version
Gradle itself 5.0 8.5+
Java plugin Built-in N/A (use java or java-library plugin)

Gradle build.gradle configuration for Java 11:

plugins {
    id 'java'
}

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

// Or using toolchains (Gradle 6.7+, preferred approach):
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(11)
    }
}

// Add module opens for tests if needed
tasks.withType(Test).configureEach {
    jvmArgs(
        '--add-opens', 'java.base/java.lang=ALL-UNNAMED',
        '--add-opens', 'java.base/java.util=ALL-UNNAMED'
    )
}

Common Library Version Requirements

Here are the minimum library versions that support Java 11:

Library Minimum Java 11 Version Notes
Spring Boot 2.1.0 Spring Framework 5.1+ required
Spring Framework 5.1.0 Full Java 11 support
Hibernate ORM 5.4.0 Earlier 5.x versions may work with --add-opens
Jackson 2.10.0 Full module system support
Lombok 1.18.4 Annotation processing updated for Java 11
Mockito 2.23.0 Uses ByteBuddy which needs Java 11 support
JUnit 5 5.4.0 JUnit 4 also works but consider migrating
Log4j 2 2.13.0 Earlier versions have Java 11 issues
Guava 27.0 Module system support added
Apache HttpClient 4.5.13 / 5.1 Consider using Java 11’s built-in HttpClient instead

Docker / CI Configuration

If you build and deploy with Docker, update your base images:

// Dockerfile -- BEFORE (Java 8)
// FROM openjdk:8-jdk-slim
// COPY target/myapp.jar /app/myapp.jar
// CMD ["java", "-jar", "/app/myapp.jar"]

// Dockerfile -- AFTER (Java 11)
// FROM eclipse-temurin:11-jdk-jammy
// COPY target/myapp.jar /app/myapp.jar
// CMD ["java", \
//      "--add-opens", "java.base/java.lang=ALL-UNNAMED", \
//      "-jar", "/app/myapp.jar"]

// For production, use JRE instead of JDK:
// FROM eclipse-temurin:11-jre-jammy
// COPY target/myapp.jar /app/myapp.jar
// CMD ["java", "-jar", "/app/myapp.jar"]

Practical Migration Example

Here is a real-world example showing code that compiles on Java 8 but fails on Java 11, along with the corrected version:

import java.util.Base64;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;

/**
 * This class demonstrates code AFTER migration to Java 11.
 * Comments show what the Java 8 version looked like.
 */
public class MigrationExample {

    // BEFORE (Java 8): Used sun.misc.BASE64Encoder
    // private static String encodeBase64(byte[] data) {
    //     return new sun.misc.BASE64Encoder().encode(data);
    // }

    // AFTER (Java 11): Use java.util.Base64
    private static String encodeBase64(byte[] data) {
        return Base64.getEncoder().encodeToString(data);
    }

    // BEFORE (Java 8): Used javax.xml.bind for hex encoding
    // private static String bytesToHex(byte[] bytes) {
    //     return javax.xml.bind.DatatypeConverter.printHexBinary(bytes);
    // }

    // AFTER (Java 11): Manual hex conversion or HexFormat (Java 17+)
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    // BEFORE (Java 8): Verbose file reading
    // private static String readFile(String path) throws Exception {
    //     byte[] bytes = java.nio.file.Files.readAllBytes(
    //         java.nio.file.Paths.get(path));
    //     return new String(bytes, java.nio.charset.StandardCharsets.UTF_8);
    // }

    // AFTER (Java 11): One-line file reading
    private static String readFile(String path) throws Exception {
        return Files.readString(Path.of(path));
    }

    // BEFORE (Java 8): Verbose blank check
    // private static boolean isNullOrBlank(String str) {
    //     return str == null || str.trim().isEmpty();
    // }

    // AFTER (Java 11): Using isBlank()
    private static boolean isNullOrBlank(String str) {
        return str == null || str.isBlank();
    }

    // BEFORE (Java 8): Collection to typed array
    // private static String[] toArray(List list) {
    //     return list.toArray(new String[0]);
    // }

    // AFTER (Java 11): Cleaner toArray
    private static String[] toArray(List list) {
        return list.toArray(String[]::new);
    }

    public static void main(String[] args) throws Exception {
        // Demonstrate all migrated methods
        System.out.println("Base64: " + encodeBase64("Hello Java 11".getBytes()));
        System.out.println("Hex: " + bytesToHex("Hi".getBytes()));
        System.out.println("isNullOrBlank(null): " + isNullOrBlank(null));
        System.out.println("isNullOrBlank(\"  \"): " + isNullOrBlank("  "));
        System.out.println("isNullOrBlank(\"hi\"): " + isNullOrBlank("hi"));

        // Write and read a file using Java 11 APIs
        Path tempFile = Path.of("migration-test.txt");
        Files.writeString(tempFile, "Java 11 migration successful!");
        System.out.println("File content: " + readFile("migration-test.txt"));

        // Cleanup
        Files.deleteIfExists(tempFile);

        // Type-safe toArray
        List names = List.of("Alice", "Bob", "Charlie");
        String[] array = toArray(names);
        System.out.println("Array: " + java.util.Arrays.toString(array));
    }
}

// Output:
// Base64: SGVsbG8gSmF2YSAxMQ==
// Hex: 4869
// isNullOrBlank(null): true
// isNullOrBlank("  "): true
// isNullOrBlank("hi"): false
// File content: Java 11 migration successful!
// Array: [Alice, Bob, Charlie]

Garbage Collector Changes

Java 11 made G1 (Garbage First) the default garbage collector, replacing the Parallel GC from Java 8. G1 is optimized for lower pause times at the cost of slightly lower throughput. For most applications, this is the right trade-off. Key differences:

Feature Parallel GC (Java 8 default) G1 GC (Java 11 default)
Optimization goal Maximum throughput Balanced throughput and latency
Pause times Can be long (seconds) Predictable, short pauses (target: 200ms)
Heap size sweet spot Small to medium heaps Medium to large heaps (4GB+)
Best for Batch processing, background jobs Web servers, microservices, interactive apps

If your application is a batch processor or throughput-critical system and you notice performance degradation after migration, you can switch back to the Parallel GC:

// Use Parallel GC (Java 8 behavior)
// java -XX:+UseParallelGC -jar myapp.jar

// Use G1 GC (Java 11 default, explicit for clarity)
// java -XX:+UseG1GC -jar myapp.jar

// Tune G1 pause target (default 200ms)
// java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar myapp.jar

// Epsilon GC (no-op collector, Java 11+) -- for benchmarking only
// java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -jar myapp.jar

// ZGC (experimental in Java 11, production-ready in Java 15)
// java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar myapp.jar

String Memory Optimization: Compact Strings

Java 9 introduced Compact Strings (enabled by default in Java 11) which stores Latin-1 strings (English, most European languages) using 1 byte per character instead of 2 bytes. This can reduce memory usage by 30-50% for string-heavy applications. This happens automatically — no code changes needed. If you suspect it causes issues (extremely rare), you can disable it with -XX:-CompactStrings.

JDK Distributions

Starting with Java 11, Oracle changed its licensing model. Oracle JDK is no longer free for commercial production use (though Oracle OpenJDK builds are). Many organizations switched to alternative distributions:

Distribution Vendor Free for Production? LTS Support
Eclipse Temurin (Adoptium) Eclipse Foundation Yes Yes (community)
Amazon Corretto Amazon Yes Yes (Amazon-backed)
Azul Zulu Azul Systems Yes (Community Edition) Yes
Red Hat OpenJDK Red Hat Yes (with RHEL) Yes
Oracle OpenJDK Oracle Yes 6 months only
Oracle JDK Oracle No (requires license) Yes (paid)

Recommendation: For most teams, Eclipse Temurin (formerly AdoptOpenJDK) or Amazon Corretto are the best choices for free, production-ready Java 11 distributions with long-term support.

Quick Reference: Migration Commands

// 1. Check current Java version
// java -version

// 2. Scan JARs for internal API usage
// jdeps --jdk-internals myapp.jar
// jdeps --jdk-internals --multi-release 11 myapp.jar

// 3. Find module dependencies
// jdeps --print-module-deps myapp.jar

// 4. Search codebase for removed APIs
// grep -r "import javax.xml.bind" src/
// grep -r "import javax.xml.ws" src/
// grep -r "import javax.annotation" src/
// grep -r "import sun.misc" src/
// grep -r "import com.sun" src/

// 5. Check Maven dependency tree for conflicts
// mvn dependency:tree
// mvn dependency:analyze

// 6. Run tests with Java 11
// mvn clean test -Dmaven.compiler.release=11

// 7. Create custom runtime image (optional, reduces deployment size)
// jlink --add-modules java.base,java.sql,java.logging \
//        --output custom-jre \
//        --strip-debug \
//        --compress 2

Final advice: The Java 8 to 11 migration is a significant effort, but it unlocks access to three years of Java improvements: the module system, new language features (var, improved try-with-resources), new APIs (HttpClient, new String methods, new file I/O), and better performance (G1 GC as default, compact strings, improved JIT compiler). The investment pays for itself quickly, especially since Java 11 is an LTS release supported until at least 2026 (extended support through 2032 from some vendors). Do not wait — the longer you stay on Java 8, the harder the eventual migration becomes.




Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


Leave a Reply

Your email address will not be published. Required fields are marked *