How to Install Java and Configure JAVA_HOME on RHEL 7

Java remains one of the most widely deployed application runtimes in enterprise environments, and RHEL 7 provides excellent support for running OpenJDK through its official repositories. Whether you are deploying a Spring Boot application, running Apache Tomcat, building microservices, or working with big data frameworks like Apache Hadoop or Spark, correctly installing and configuring the Java Development Kit (JDK) is a foundational skill for RHEL 7 administrators. This tutorial walks you through installing OpenJDK 11, configuring JAVA_HOME, managing multiple JDK versions with update-alternatives, compiling and running a Java application, tuning JVM flags, and using diagnostic tools to inspect running JVM processes.

Prerequisites

  • RHEL 7 with a valid subscription or access to a configured yum repository
  • sudo or root access
  • At least 1 GB of free disk space
  • Internet access or a local repository mirror

Step 1: Install OpenJDK 11 with yum

RHEL 7 repositories include several OpenJDK versions. The java-11-openjdk-devel package installs the full JDK including the javac compiler, whereas java-11-openjdk installs only the JRE. For development and build environments, always install the -devel variant.

# Update package metadata
sudo yum update -y

# Install OpenJDK 11 JDK
sudo yum install -y java-11-openjdk-devel

# Verify the installation
java -version
javac -version

You should see output similar to:

openjdk version "11.0.22" 2024-01-16 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.22+7-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.22+7-LTS, mixed mode, sharing)

To install OpenJDK 8 for applications that require older compatibility:

sudo yum install -y java-1.8.0-openjdk-devel

Step 2: Configure JAVA_HOME System-Wide

Many Java applications, build tools (Maven, Gradle, Ant), and application servers rely on the JAVA_HOME environment variable to locate the JDK. Configure it system-wide using a profile drop-in script so it is available to all users and services.

# Find the actual JDK installation path
java_path=$(readlink -f $(which java))
java_home=$(dirname $(dirname $java_path))
echo "Detected JAVA_HOME: $java_home"

# Create the profile script
sudo tee /etc/profile.d/java.sh <<EOF
# Java environment configuration
export JAVA_HOME=${java_home}
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
EOF

# Apply to the current session
source /etc/profile.d/java.sh

# Verify
echo "JAVA_HOME=$JAVA_HOME"
java -version

For OpenJDK 11 on RHEL 7, JAVA_HOME is typically /usr/lib/jvm/java-11-openjdk-11.0.22.0.7-1.el7_9.x86_64. The readlink -f approach automatically resolves the symlink to the current active version.

Step 3: Manage Multiple JDK Versions with update-alternatives

RHEL 7 uses the alternatives system (part of chkconfig) to manage multiple versions of the same command. If you have both JDK 8 and JDK 11 installed, you can switch between them without uninstalling either.

# List all available java alternatives
sudo alternatives --list | grep java

# Interactive selection — choose the desired JDK from a numbered menu
sudo alternatives --config java
sudo alternatives --config javac

The interactive prompt will display all registered Java versions. Enter the number corresponding to the version you want to activate. To switch programmatically (useful in scripts):

# Switch to Java 11 non-interactively
sudo alternatives --set java /usr/lib/jvm/java-11-openjdk-11.0.22.0.7-1.el7_9.x86_64/bin/java
sudo alternatives --set javac /usr/lib/jvm/java-11-openjdk-11.0.22.0.7-1.el7_9.x86_64/bin/javac

# Verify the active version
java -version
javac -version

# Update JAVA_HOME to match the new active version after switching
source /etc/profile.d/java.sh

Step 4: Compile and Run a Java Application

Confirm that the JDK is fully functional by compiling and running a basic Java program.

mkdir -p ~/javatest
cat > ~/javatest/HelloWorld.java <<'EOF'
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello from OpenJDK " + System.getProperty("java.version") +
                           " on " + System.getProperty("os.name"));

        // Print all system properties
        if (args.length > 0 && args[0].equals("--verbose")) {
            System.getProperties().forEach((k, v) ->
                System.out.printf("  %s = %s%n", k, v));
        }
    }
}
EOF

# Compile
javac ~/javatest/HelloWorld.java -d ~/javatest/

# Verify the class file was created
ls -lh ~/javatest/HelloWorld.class

# Run
java -cp ~/javatest HelloWorld
java -cp ~/javatest HelloWorld --verbose

For applications packaged as JAR files:

# Create a manifest file
cat > ~/javatest/MANIFEST.MF <<'EOF'
Main-Class: HelloWorld
EOF

# Package into a JAR
jar cfm ~/javatest/hello.jar ~/javatest/MANIFEST.MF -C ~/javatest/ HelloWorld.class

# Run the JAR
java -jar ~/javatest/hello.jar

Step 5: JVM Tuning with Memory and GC Flags

The Java Virtual Machine offers extensive configuration flags for memory management and garbage collection. Understanding these is critical for tuning application servers and batch processing jobs on RHEL 7.

# Set initial and maximum heap size
java -Xms256m -Xmx1g -cp ~/javatest HelloWorld

# Use the G1 Garbage Collector (recommended for JDK 11+)
java -XX:+UseG1GC -Xms512m -Xmx2g -cp ~/javatest HelloWorld

# Verbose GC logging for performance analysis
java -XX:+UseG1GC 
     -Xms512m -Xmx2g 
     -XX:+PrintGCDetails 
     -XX:+PrintGCDateStamps 
     -Xloggc:/var/log/myapp/gc.log 
     -cp ~/javatest HelloWorld

# Set G1GC pause time target (milliseconds)
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -cp ~/javatest HelloWorld

Key JVM flags explained:

  • -Xms: Initial heap size. Setting this equal to -Xmx avoids heap resizing overhead at the cost of always allocating the full amount.
  • -Xmx: Maximum heap size. Never set this above 75% of available system RAM.
  • -XX:+UseG1GC: Enable the G1 (Garbage First) collector, which provides predictable pause times and is the default in Java 9+.
  • -XX:MaxGCPauseMillis: Target maximum pause time for G1GC. Lower values trade throughput for latency.

Step 6: JVM Diagnostics with jps and jstack

OpenJDK ships with several command-line diagnostic tools. Two of the most useful for day-to-day administration are jps (Java process status) and jstack (Java thread dump).

# Start a long-running Java process in the background for demonstration
java -cp ~/javatest HelloWorld &
JAVA_PID=$!

# List all running JVM processes (similar to `ps` but Java-specific)
jps
jps -lvm

# Get a full thread dump from a running JVM
jstack $JAVA_PID

# Get a thread dump and save to file for analysis
jstack $JAVA_PID > /tmp/threaddump_$(date +%Y%m%d_%H%M%S).txt

# Check heap usage with jmap
jmap -heap $JAVA_PID

# Generate a heap dump for memory analysis
jmap -dump:format=b,file=/tmp/heapdump.hprof $JAVA_PID

# Kill the background process
kill $JAVA_PID

Thread dumps are invaluable for diagnosing deadlocks and high CPU usage in production. If a Java application is hung, take three thread dumps 10 seconds apart to identify which threads are consistently blocked.

Step 7: Running Java Applications as systemd Services

Production Java applications on RHEL 7 should be managed by systemd. Create a unit file that respects JAVA_HOME and passes appropriate JVM flags.

sudo tee /etc/systemd/system/myapp.service <<'EOF'
[Unit]
Description=My Java Application
After=network.target

[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk"
Environment="JAVA_OPTS=-Xms256m -Xmx1g -XX:+UseG1GC"
ExecStart=/usr/lib/jvm/java-11-openjdk/bin/java $JAVA_OPTS -jar /opt/myapp/myapp.jar
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable myapp.service
sudo systemctl start myapp.service
sudo systemctl status myapp.service

Conclusion

You have installed OpenJDK 11 on RHEL 7 using yum, configured JAVA_HOME and PATH system-wide via /etc/profile.d/java.sh, and learned to manage multiple JDK installations with update-alternatives. You compiled and packaged a Java application, applied critical JVM tuning flags including heap sizing and G1GC configuration, and used jps and jstack for runtime diagnostics. Finally, you configured a systemd service unit to manage a Java application as a reliable system service. These skills form the foundation for deploying and maintaining any Java-based workload on RHEL 7 in production.