Tomcat 内存调优

Memory Tuning

概述

内存调优是Tomcat性能优化的核心。本文介绍JVM内存配置、垃圾回收优化、内存泄漏检测与解决方案。

1. JVM内存配置策略

1.1 基础内存配置

# setenv.sh - 不同规模应用的内存配置

# 小型应用 (1-2GB)
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m -Xmx1024m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:NewRatio=4"

# 中型应用 (2-8GB)
export CATALINA_OPTS="$CATALINA_OPTS -Xms2048m -Xmx4096m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:NewRatio=3 -XX:SurvivorRatio=8"

# 大型应用 (8GB+)
export CATALINA_OPTS="$CATALINA_OPTS -Xms8192m -Xmx8192m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=1024m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:NewRatio=2"

1.2 自动内存配置脚本

#!/bin/bash
# auto-memory-config.sh

get_optimal_memory() {
    total_mem_gb=$(free -g | awk 'NR==2{print $2}')

    if [ $total_mem_gb -le 2 ]; then
        heap_size="1024m"; metaspace="256m"
    elif [ $total_mem_gb -le 4 ]; then
        heap_size="2048m"; metaspace="512m"
    elif [ $total_mem_gb -le 8 ]; then
        heap_size="4096m"; metaspace="512m"
    else
        heap_size="8192m"; metaspace="1024m"
    fi

    cat > "$CATALINA_HOME/bin/setenv.sh" << EOF
export CATALINA_OPTS="\$CATALINA_OPTS -Xms$heap_size -Xmx$heap_size"
export CATALINA_OPTS="\$CATALINA_OPTS -XX:MetaspaceSize=$metaspace"
EOF
    echo "已配置: 堆=$heap_size, 元空间=$metaspace"
}

get_optimal_memory

2. 垃圾回收器优化

2.1 G1GC配置

# G1GC - 推荐用于大堆内存
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseG1GC"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxGCPauseMillis=200"
export CATALINA_OPTS="$CATALINA_OPTS -XX:G1HeapRegionSize=16m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:G1NewSizePercent=20"
export CATALINA_OPTS="$CATALINA_OPTS -XX:G1MaxNewSizePercent=40"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=45"

2.2 ParallelGC配置

# ParallelGC - 适用于吞吐量优先
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseParallelGC"
export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=8"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxGCPauseMillis=150"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseAdaptiveSizePolicy"

2.3 GC日志配置

# Java 8 GC日志
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDetails"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCTimeStamps"
export CATALINA_OPTS="$CATALINA_OPTS -Xloggc:$CATALINA_HOME/logs/gc.log"

# Java 11+ GC日志
export CATALINA_OPTS="$CATALINA_OPTS -Xlog:gc*:$CATALINA_HOME/logs/gc.log:time"

3. 内存监控

3.1 内存监控工具

// MemoryMonitor.java
package com.example.memory;

import java.lang.management.*;

public class MemoryMonitor {

    public void printMemoryInfo() {
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();

        // 堆内存
        MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
        double usedMB = heapUsage.getUsed() / 1024.0 / 1024.0;
        double maxMB = heapUsage.getMax() / 1024.0 / 1024.0;
        double percent = (heapUsage.getUsed() * 100.0) / heapUsage.getMax();

        System.out.printf("堆内存: %.2f MB / %.2f MB (%.1f%%)%n", usedMB, maxMB, percent);

        // GC统计
        for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
            System.out.printf("GC %s: 次数=%d, 时间=%dms%n",
                gcBean.getName(), gcBean.getCollectionCount(), gcBean.getCollectionTime());
        }
    }

    public boolean isMemoryPressureHigh() {
        MemoryUsage usage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
        return (usage.getUsed() * 100.0 / usage.getMax()) > 80.0;
    }
}

3.2 内存监控脚本

#!/bin/bash
# memory-monitor.sh

JAVA_PID=$(jps -l | grep Bootstrap | awk '{print $1}')

monitor_memory() {
    if [ -z "$JAVA_PID" ]; then
        echo "Tomcat进程未找到"
        return
    fi

    echo "=== 内存监控 $(date) ==="

    # 堆内存使用
    jstat -gc $JAVA_PID | awk 'NR==2 {
        used = ($2 + $3 + $4) / 1024
        total = ($1 + $2 + $3 + $4 + $5) / 1024
        printf "堆内存: %.2f MB / %.2f MB (%.1f%%)\n", used, total, used*100/total
    }'

    # GC统计
    jstat -gccapacity $JAVA_PID | awk 'NR==2 {
        printf "新生代容量: %.2f MB, 老年代容量: %.2f MB\n", $4/1024, $10/1024
    }'
}

case "$1" in
    "once") monitor_memory ;;
    "watch") 
        while true; do
            monitor_memory
            sleep 30
        done
        ;;
    *) echo "用法: $0 {once|watch}" ;;
esac

4. 内存泄漏检测

4.1 内存泄漏检测脚本

#!/bin/bash
# leak-detector.sh

JAVA_PID=$(jps -l | grep Bootstrap | awk '{print $1}')

# 生成堆转储
generate_heap_dump() {
    timestamp=$(date +%Y%m%d_%H%M%S)
    dump_file="/tmp/tomcat_heap_$timestamp.hprof"

    echo "生成堆转储: $dump_file"
    jcmd $JAVA_PID GC.run
    jcmd $JAVA_PID VM.gc
    jhsdb jmap --heap-dump="$dump_file" --pid $JAVA_PID
    echo "堆转储完成: $dump_file"
}

# 分析内存使用
analyze_memory() {
    echo "=== 内存使用分析 ==="

    # 堆直方图
    jcmd $JAVA_PID GC.class_histogram | head -20

    # 线程分析
    echo "线程状态统计:"
    jstack $JAVA_PID | grep "java.lang.Thread.State" | sort | uniq -c
}

case "$1" in
    "dump") generate_heap_dump ;;
    "analyze") analyze_memory ;;
    *) echo "用法: $0 {dump|analyze}" ;;
esac

4.2 内存泄漏预防

// MemoryLeakPrevention.java
package com.example.memory;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.Enumeration;

public class MemoryLeakPrevention implements ServletContextListener {

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 清理JDBC驱动
        cleanupJDBCDrivers();
        // 清理线程
        cleanupThreads();
        // 强制GC
        System.gc();
    }

    private void cleanupJDBCDrivers() {
        try {
            Enumeration<Driver> drivers = DriverManager.getDrivers();
            while (drivers.hasMoreElements()) {
                Driver driver = drivers.nextElement();
                if (driver.getClass().getClassLoader() == getClass().getClassLoader()) {
                    DriverManager.deregisterDriver(driver);
                }
            }
        } catch (Exception e) {
            System.err.println("清理JDBC驱动失败: " + e.getMessage());
        }
    }

    private void cleanupThreads() {
        Thread[] threads = new Thread[Thread.activeCount()];
        Thread.enumerate(threads);

        for (Thread thread : threads) {
            if (thread != null && thread.isAlive() && 
                !thread.isDaemon() && 
                thread.getClass().getClassLoader() == getClass().getClassLoader()) {
                thread.interrupt();
            }
        }
    }
}

5. 内存优化最佳实践

5.1 JVM参数优化

# 生产环境推荐配置
export CATALINA_OPTS="$CATALINA_OPTS -server"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+DisableExplicitGC"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseCompressedOops"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseStringDeduplication"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+OptimizeStringConcat"

5.2 内存监控告警

#!/bin/bash
# memory-alert.sh

check_memory_usage() {
    JAVA_PID=$(jps -l | grep Bootstrap | awk '{print $1}')
    THRESHOLD=80

    usage=$(jstat -gc $JAVA_PID | awk 'NR==2 {
        used=($2+$3+$4); total=($1+$2+$3+$4+$5)
        print int(used*100/total)
    }')

    if [ $usage -gt $THRESHOLD ]; then
        echo "警告: 内存使用率 ${usage}% 超过阈值 ${THRESHOLD}%"
        # 发送告警邮件
        echo "Tomcat内存使用率过高: ${usage}%" | mail -s "内存告警" admin@example.com
    fi
}

check_memory_usage

5.3 内存优化清单

#!/bin/bash
# memory-checklist.sh

echo "=== Tomcat内存优化检查清单 ==="

check_item() {
    local desc="$1"
    local cmd="$2"

    if eval "$cmd"; then
        echo "✓ $desc"
    else
        echo "✗ $desc"
    fi
}

# 检查JVM参数
check_item "设置了初始堆大小" "ps aux | grep java | grep -q Xms"
check_item "设置了最大堆大小" "ps aux | grep java | grep -q Xmx"
check_item "配置了元空间" "ps aux | grep java | grep -q MetaspaceSize"
check_item "启用了压缩指针" "ps aux | grep java | grep -q UseCompressedOops"

# 检查GC配置
check_item "配置了GC日志" "ps aux | grep java | grep -q 'gc.*log'"
check_item "选择了合适的GC" "ps aux | grep java | grep -qE 'UseG1GC|UseParallelGC'"

echo "内存优化检查完成"

小结

通过本文学习,你应该掌握:

  1. JVM内存配置策略和最佳实践
  2. 不同垃圾回收器的选择和调优
  3. 内存使用监控和分析方法
  4. 内存泄漏检测和预防技术
  5. 生产环境内存优化配置
  6. 内存问题诊断和解决方案

下一篇文章将介绍Tomcat集群配置技术。

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-08-29 15:40:15

results matching ""

    No results matching ""