1. 首页 > WebSphere教程 > 正文

WebSphere教程FG016-WebSphere内存泄漏问题排查与解决实战

本文档风哥主要介绍WebSphere Application Server 9.0.5的内存泄漏问题排查与解决,包括内存泄漏类型、原因分析、检测方法、堆转储分析、修复方案等内容,风哥教程参考WebSphere官方文档故障排查章节,适合WebSphere管理员在学习和测试中使用,如果要应用于生产环境则需要自行确认。更多视频教程www.fgedu.net.cn

Part01-基础概念与理论知识

1.1 内存泄漏概述

内存泄漏是指程序在运行过程中动态分配的内存由于某种原因未能释放,导致内存占用持续增长,最终可能导致系统性能下降甚至崩溃。学习交流加群风哥微信: itpux-com

内存泄漏特征:

  • 持续增长:内存使用量持续上升
  • GC无效:垃圾回收无法回收内存
  • 性能下降:应用响应变慢
  • 最终OOM:内存溢出错误

1.1.1 JVM内存模型

# JVM内存模型

┌─────────────────────────────────────────────────────────┐
│ JVM堆内存 │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────┐ │
│ │ 年轻代(Young Generation) │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Eden │ │ Survivor│ │ Survivor│ │ │
│ │ │ Space │ │ S0 │ │ S1 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 老年代(Old Generation) │ │
│ │ │ │
│ │ 长期存活对象、大对象 │ │
│ │ │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

内存泄漏通常发生在老年代:
– 对象被持续创建但无法被回收
– 老年代空间持续增长
– 频繁Full GC但效果不明显

# 内存泄漏与内存溢出

内存泄漏(Memory Leak):
– 对象不再使用但无法被回收
– 内存占用持续增长
– 是内存溢出的原因之一

内存溢出(OutOfMemory):
– 内存不足无法分配新对象
– 可能由内存泄漏导致
– 也可能是内存配置不足

# 内存泄漏判断标准

1. 内存使用趋势
– 正常:内存使用波动,GC后下降
– 泄漏:内存使用持续上升,GC后不下降

2. GC行为
– 正常:Young GC频繁,Full GC较少
– 泄漏:Full GC频繁,每次回收效果差

3. 对象存活
– 正常:老年代对象稳定
– 泄漏:老年代对象持续增长

1.2 内存泄漏类型

内存泄漏类型:

1.2.1 常见泄漏类型

# 内存泄漏类型

1. 静态集合类泄漏
原因:静态集合持有对象引用
示例:
public class FGeduCache {
private static Map cache = new HashMap<>();

public void put(String key, Object value) {
cache.put(key, value); // 对象无法被回收
}
}

2. 监听器未注销
原因:注册监听器后未注销
示例:
public class FGeduListener {
public void register() {
eventSource.addListener(this); // 未注销
}
}

3. 连接未关闭
原因:数据库连接、IO流未关闭
示例:
public void query() {
Connection conn = dataSource.getConnection();
// 使用连接…
// 忘记关闭连接
}

4. ThreadLocal泄漏
原因:ThreadLocal未清理
示例:
public class FGeduContext {
private static ThreadLocal context = new ThreadLocal<>();

public void set(Object value) {
context.set(value); // 线程池复用时泄漏
}
}

5. 类加载器泄漏
原因:类加载器未正确卸载
示例:
– 应用热部署时旧类加载器未卸载
– 导致类和静态变量无法回收

# 内存泄漏分类

类型 描述 严重程度
──────────────────────────────────────────────────────
堆内存泄漏 对象无法被GC回收 高
直接内存泄漏 DirectBuffer未释放 中
本地内存泄漏 Native内存泄漏 高
永久代/元空间泄漏 类加载过多 中

1.3 内存泄漏原因

内存泄漏原因:

1.3.1 泄漏原因分析

# 内存泄漏原因

1. 编码问题
– 静态集合持有对象引用
– 监听器未注销
– 连接资源未关闭
– ThreadLocal未清理
– 匿名内部类持有外部类引用

2. 框架问题
– 框架缓存配置不当
– 框架对象生命周期管理问题
– 框架Bug导致泄漏

3. 应用服务器问题
– 类加载器泄漏
– 线程池线程复用
– 会话管理问题

4. 第三方库问题
– 第三方库Bug
– 第三方库配置不当

# 内存泄漏常见场景

场景 原因 解决方案
──────────────────────────────────────────────────────
缓存无限增长 无淘汰机制 设置缓存大小限制
数据库连接池耗尽 连接未关闭 使用try-with-resources
线程池线程泄漏 线程未正确结束 合理管理线程生命周期
会话对象泄漏 会话未失效 设置会话超时
日志对象泄漏 日志框架配置问题 合理配置日志级别

1.4 内存分析工具

内存分析工具:

1.4.1 常用工具介绍

# 内存分析工具

1. IBM HeapAnalyzer
用途:分析WebSphere堆转储
特点:IBM官方工具,支持PHD格式
下载:IBM Support Portal

2. Eclipse Memory Analyzer (MAT)
用途:分析堆转储,查找内存泄漏
特点:功能强大,支持多种格式
下载:https://eclipse.dev/mat/

3. IBM Monitoring and Diagnostic Tools
用途:WebSphere专用诊断工具
特点:集成在WebSphere中
位置:/WebSphere/app/IBM/mt

4. VisualVM
用途:实时监控JVM
特点:JDK自带,实时监控
启动:jvisualvm

5. JConsole
用途:JMX监控
特点:JDK自带,简单易用
启动:jconsole

# 工具对比

工具 优点 缺点
──────────────────────────────────────────────────────
HeapAnalyzer 支持PHD格式 界面老旧
MAT 功能强大 内存消耗大
IBM MT 集成度高 需要安装
VisualVM 实时监控 分析功能有限
JConsole 简单易用 功能有限

# 推荐组合
实时监控:VisualVM + JConsole
离线分析:MAT + HeapAnalyzer

风哥提示:内存泄漏排查需要结合实时监控和离线分析,建议先通过监控发现问题,再通过堆转储分析定位具体原因。

Part02-生产环境规划与建议

2.1 内存泄漏预防规划

内存泄漏预防规划:

2.1.1 预防措施

# 内存泄漏预防措施

1. 编码规范
– 使用try-with-resources关闭资源
– 及时注销监听器
– 清理ThreadLocal
– 避免静态集合无限增长

2. 代码审查
– 审查资源关闭代码
– 审查缓存使用
– 审查监听器注册/注销
– 审查ThreadLocal使用

3. 测试验证
– 压力测试监控内存
– 长时间运行测试
– 内存泄漏检测工具

4. 配置优化
– 合理设置堆内存大小
– 配置GC策略
– 设置缓存大小限制

# 预防检查清单

编码阶段:
□ 资源关闭使用try-with-resources
□ 缓存设置大小限制
□ 监听器成对注册/注销
□ ThreadLocal使用后清理

测试阶段:
□ 压力测试内存监控
□ 长时间运行测试
□ 内存泄漏检测

部署阶段:
□ 合理配置堆内存
□ 配置GC日志
□ 设置内存告警

2.2 内存监控规划

内存监控规划:

2.2.1 监控配置

# 内存监控配置

1. 启用GC日志
管理控制台 > 服务器 > server1 > Java虚拟机
> 通用JVM参数:
-Xlog:gc*:file=/WebSphere/logs/gc.log:time,uptime,level,tags:filecount=10,filesize=100m

2. 启用PMI监控
管理控制台 > 服务器 > server1 > 性能监控基础设施
> 启用性能监控基础设施
> 监控级别:Medium

3. 配置内存告警
指标 警告阈值 严重阈值
──────────────────────────────────────────────────────
堆内存使用率 80% 90%
老年代使用率 80% 90%
Full GC频率 > 1次/分钟 > 5次/分钟
GC暂停时间 > 1秒 > 5秒

# 监控脚本
#!/bin/bash
# memory_monitor.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn

LOG_FILE=/WebSphere/logs/memory_monitor.log
ALERT_THRESHOLD=80

while true; do
# 获取内存使用率
HEAP_USAGE=$(/WebSphere/app/profiles/Dmgr01/bin/wsadmin.sh -lang jython \
-username fgeduadmin -password fgedu123 -c ”
jvm = AdminControl.queryNames(‘type=JVM,process=server1,*’)
heap = AdminControl.getAttribute(jvm, ‘heapMemoryUsage’)
print heap.get(‘used’) * 100 / heap.get(‘max’)
” 2>/dev/null)

echo “$(date): 堆内存使用率 ${HEAP_USAGE}%” >> $LOG_FILE

# 告警判断
if [ $(echo “$HEAP_USAGE > $ALERT_THRESHOLD” | bc) -eq 1 ]; then
echo “WebSphere内存告警:堆内存使用率${HEAP_USAGE}%” | \
mail -s “WebSphere内存告警” admin@fgedu.net.cn
fi

sleep 300
done

2.3 排查策略规划

排查策略规划:

2.3.1 排查流程

# 内存泄漏排查流程

1. 问题确认
– 监控内存使用趋势
– 分析GC日志
– 确认是否为内存泄漏

2. 数据收集
– 生成堆转储
– 收集GC日志
– 收集应用日志

3. 数据分析
– 使用MAT分析堆转储
– 查找大对象
– 分析对象引用链

4. 定位原因
– 确定泄漏对象类型
– 分析对象创建位置
– 确定泄漏原因

5. 制定方案
– 代码修复
– 配置优化
– 架构调整

6. 验证修复
– 测试验证
– 监控验证
– 压力测试

# 排查优先级

优先级 内容 时间
──────────────────────────────────────────────────────
P1 确认问题 30分钟
P2 收集数据 1小时
P3 分析数据 2小时
P4 定位原因 2小时
P5 制定方案 1小时
P6 验证修复 2小时

2.4 告警机制规划

告警机制规划:

2.4.1 告警配置

# 内存告警配置

1. 告警阈值
指标 Warning Major Critical
──────────────────────────────────────────────────────
堆内存使用率 75% 85% 95%
老年代使用率 75% 85% 95%
Full GC频率(次/分钟) 1 3 5
GC暂停时间(秒) 1 3 5
内存增长速率(MB/小时) 100 200 500

2. 告警通知
– 邮件通知
– 短信通知
– 企业微信/钉钉

3. 告警升级
– 15分钟未处理:升级上级
– 30分钟未处理:升级经理
– 1小时未处理:升级总监

# 告警脚本
#!/bin/bash
# memory_alert.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn

HEAP_USAGE=$1
ALERT_LEVEL=$2

send_alert() {
local level=$1
local msg=$2

case $level in
Critical)
echo “$msg” | mail -s “WebSphere严重告警” admin@fgedu.net.cn
curl -X POST “http://sms.fgedu.net.cn/send” -d “phone=13800138000&msg=$msg”
;;
Major)
echo “$msg” | mail -s “WebSphere重要告警” admin@fgedu.net.cn
;;
Warning)
echo “$msg” | mail -s “WebSphere警告” admin@fgedu.net.cn
;;
esac
}

if [ $(echo “$HEAP_USAGE > 95” | bc) -eq 1 ]; then
send_alert “Critical” “WebSphere堆内存使用率${HEAP_USAGE}%,请立即处理!”
elif [ $(echo “$HEAP_USAGE > 85” | bc) -eq 1 ]; then
send_alert “Major” “WebSphere堆内存使用率${HEAP_USAGE}%,请尽快处理!”
elif [ $(echo “$HEAP_USAGE > 75” | bc) -eq 1 ]; then
send_alert “Warning” “WebSphere堆内存使用率${HEAP_USAGE}%,请关注!”
fi

Part03-生产环境项目实施方案

3.1 内存泄漏检测实战

内存泄漏检测操作:

3.1.1 监控内存趋势

# 监控内存趋势

1. 使用管理控制台监控
管理控制台 > 监控和调整 > 性能监控器
选择服务器:server1
查看JVM运行时指标

2. 使用命令行监控
/WebSphere/app/profiles/Dmgr01/bin/wsadmin.sh -lang jython \
-username fgeduadmin -password fgedu123

# 获取堆内存信息
jvm = AdminControl.queryNames(‘type=JVM,process=server1,*’)
heap = AdminControl.getAttribute(jvm, ‘heapMemoryUsage’)
print “已用堆内存:” + str(heap.get(‘used’) / 1024 / 1024) + ” MB”
print “最大堆内存:” + str(heap.get(‘max’) / 1024 / 1024) + ” MB”
print “使用率:” + str(heap.get(‘used’) * 100 / heap.get(‘max’)) + “%”

已用堆内存:1536 MB
最大堆内存:2048 MB
使用率:75%

# 获取GC信息
runtime = AdminControl.queryNames(‘type=JVMRuntime,process=server1,*’)
print “GC次数:” + AdminControl.getAttribute(runtime, ‘gcCount’)
print “GC时间:” + AdminControl.getAttribute(runtime, ‘gcTime’) + ” ms”

GC次数:1250
GC时间:3500 ms

3. 分析GC日志
grep “Full GC” /WebSphere/logs/gc.log | tail -10

[2026-04-10T10:00:00.000+0800][gc] GC(100) Pause Full (Allocation Failure)
[2026-04-10T10:00:05.234+0800][gc] GC(100) 1900M->1800M(2048M), 5.234ms

# 内存泄漏判断
– Full GC后内存回收效果差(1900M->1800M)
– Full GC频率高
– 内存持续增长

4. 使用VisualVM监控
# 启动VisualVM
jvisualvm

# 添加JMX连接
文件 > 添加JMX连接
URL: service:jmx:rmi:///jndi/rmi://fgedu.net.cn:9100/jmxrmi
用户名:fgeduadmin
密码:fgedu123

# 查看内存趋势
监控 > 堆
观察堆内存使用曲线

# 内存泄漏特征曲线
正常曲线:锯齿状,GC后下降
泄漏曲线:阶梯状上升,GC后不下降

3.2 堆转储分析实战

堆转储分析操作:

3.2.1 生成堆转储

# 生成堆转储

1. 通过wsadmin生成
/WebSphere/app/profiles/Dmgr01/bin/wsadmin.sh -lang jython \
-username fgeduadmin -password fgedu123

# 生成堆转储
jvm = AdminControl.queryNames(‘type=JVM,process=server1,*’)
AdminControl.invoke(jvm, ‘generateHeapDump’)

JVMDUMP013I 已处理堆转储请求
JVMDUMP010I 已将堆转储写入 /WebSphere/app/profiles/AppSrv01/heapdump.20260410.100000.12345.phd

2. 通过kill命令生成
# 获取进程ID
ps -ef | grep java | grep server1

fgedu 12345 1 5 10:00 ? 00:00:30 /WebSphere/app/java/bin/java …

# 生成堆转储
kill -3 12345

# 堆转储文件位置
/WebSphere/app/profiles/AppSrv01/heapdump.20260410.100000.12345.phd

3. 通过管理控制台生成
管理控制台 > 故障诊断 > Java转储和核心
> 选择服务器:server1
> 点击”生成堆转储”

4. 堆转储文件说明
文件类型:
– .phd:IBM格式,需要HeapAnalyzer分析
– .hprof:标准格式,可用MAT分析

文件大小:
– 约等于堆内存大小
– 生成时可能需要额外内存

注意事项:
– 生成堆转储会暂停应用
– 确保磁盘空间充足
– 选择业务低峰期操作

3.2.2 分析堆转储

# 分析堆转储

1. 使用IBM HeapAnalyzer
# 下载HeapAnalyzer
wget https://public.dhe.ibm.com/software/websphere/appserv/support/tools/HeapAnalyzer/ha456.jar

# 启动分析
java -Xmx4g -jar ha456.jar

# 打开堆转储文件
File > Open > 选择heapdump.phd文件

# 分析结果
┌─────────────────────────────────────────────────────────┐
│ HeapAnalyzer分析结果 │
├─────────────────────────────────────────────────────────┤
│ 总堆大小:2048 MB │
│ 对象数量:1,234,567 │
│ 类数量:12,345 │
│ │
│ 疑似内存泄漏: │
│ 1. FGeduCache – 500 MB (25%) │
│ – HashMap$Entry: 100,000个 │
│ – 引用链:FGeduCache.cache -> HashMap │
│ │
│ 2. FGeduSession – 300 MB (15%) │
│ – FGeduSession对象:50,000个 │
│ – 引用链:SessionManager.sessions -> HashMap │
└─────────────────────────────────────────────────────────┘

2. 使用MAT分析
# 启动MAT
./mat -vmargs -Xmx4g

# 打开堆转储
File > Open Heap Dump > 选择heapdump.hprof

# 执行泄漏检测
Leak Suspects Report

# 分析结果
┌─────────────────────────────────────────────────────────┐
│ MAT泄漏检测报告 │
├─────────────────────────────────────────────────────────┤
│ Problem Suspect 1 │
│ The class java.util.HashMap loaded by │
│ com.ibm.ws.classloader.CompoundClassLoader │
│ occupies 500 MB (25%) of the heap. │
│ │
│ Keywords: │
│ java.util.HashMap │
│ com.fgedu.cache.FGeduCache │
│ │
│ Problem Suspect 2 │
│ The class com.fgedu.session.FGeduSession │
│ loaded by com.ibm.ws.classloader.CompoundClassLoader │
│ occupies 300 MB (15%) of the heap. │
└─────────────────────────────────────────────────────────┘

# 查看对象引用链
右键对象 > Path to GC Roots > exclude weak/soft references

# 查看直方图
Window > Histogram
按对象数量或大小排序

# 查看支配树
Window > Dominator Tree
找出占用内存最多的对象

3.3 内存泄漏修复实战

内存泄漏修复操作:

3.3.1 常见泄漏修复

# 内存泄漏修复

1. 缓存泄漏修复
// 问题代码
public class FGeduCache {
private static Map cache = new HashMap<>();

public void put(String key, Object value) {
cache.put(key, value); // 无限增长
}
}

// 修复方案1:使用LRU缓存
public class FGeduCache {
private static final int MAX_SIZE = 10000;
private static Map cache =
new LinkedHashMap(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_SIZE;
}
};
}

// 修复方案2:使用WeakHashMap
public class FGeduCache {
private static Map cache = new WeakHashMap<>();
}

// 修复方案3:使用Guava Cache
public class FGeduCache {
private static Cache cache = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterAccess(1, TimeUnit.HOURS)
.build();
}

2. 连接泄漏修复
// 问题代码
public void query() {
Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(“SELECT * FROM fgedu_user”);
// 处理结果…
// 忘记关闭连接
}

// 修复方案:使用try-with-resources
public void query() {
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(“SELECT * FROM fgedu_user”)) {
// 处理结果…
} catch (SQLException e) {
logger.error(“查询失败”, e);
}
}

3. ThreadLocal泄漏修复
// 问题代码
public class FGeduContext {
private static ThreadLocal context = new ThreadLocal<>();

public void set(Object value) {
context.set(value); // 线程池复用时泄漏
}
}

// 修复方案:使用后清理
public class FGeduContext {
private static ThreadLocal context = new ThreadLocal<>();

public void set(Object value) {
context.set(value);
}

public Object get() {
return context.get();
}

public void remove() {
context.remove(); // 使用后清理
}
}

// 使用示例
try {
FGeduContext.set(data);
// 业务处理…
} finally {
FGeduContext.remove(); // 确保清理
}

4. 监听器泄漏修复
// 问题代码
public class FGeduListener implements EventListener {
public void register() {
eventSource.addListener(this); // 未注销
}
}

// 修复方案:成对注册/注销
public class FGeduListener implements EventListener {
public void register() {
eventSource.addListener(this);
}

public void unregister() {
eventSource.removeListener(this); // 注销监听器
}
}

// 使用示例
FGeduListener listener = new FGeduListener();
try {
listener.register();
// 业务处理…
} finally {
listener.unregister(); // 确保注销
}

3.4 修复验证实战

修复验证操作:

3.4.1 验证步骤

# 修复验证

1. 部署修复后的应用
# 停止应用
/WebSphere/app/profiles/Dmgr01/bin/wsadmin.sh -lang jython \
-username fgeduadmin -password fgedu123 \
-c “AdminControl.invoke(AdminControl.queryNames(‘type=ApplicationManager,process=server1,*’), ‘stopApplication’, ‘fgedu-app’)”

# 更新应用
AdminApp.update(‘fgedu-app’, ‘app’, ‘[-contents /WebSphere/apps/fgedu-app-fixed.war]’)

# 启动应用
AdminControl.invoke(AdminControl.queryNames(‘type=ApplicationManager,process=server1,*’),
‘startApplication’, ‘fgedu-app’)

2. 监控内存使用
# 持续监控24小时
while true; do
HEAP_USAGE=$(/WebSphere/app/profiles/Dmgr01/bin/wsadmin.sh -lang jython \
-username fgeduadmin -password fgedu123 -c ”
jvm = AdminControl.queryNames(‘type=JVM,process=server1,*’)
heap = AdminControl.getAttribute(jvm, ‘heapMemoryUsage’)
print heap.get(‘used’) * 100 / heap.get(‘max’)
” 2>/dev/null)

echo “$(date): 堆内存使用率 ${HEAP_USAGE}%” >> /WebSphere/logs/memory_verify.log
sleep 300
done

3. 压力测试验证
# 使用JMeter进行压力测试
jmeter -n -t fgedu_test.jmx -l result.jtl -e -o report

# 监控内存
# 测试前:堆内存使用率 30%
# 测试中:堆内存使用率 60%
# 测试后:堆内存使用率 35%(正常回收)

4. 对比分析
修复前:
– 堆内存持续增长
– Full GC频繁
– 内存回收效果差

修复后:
– 堆内存稳定
– Full GC减少
– 内存回收正常

# 验证结果
┌─────────────────────────────────────────────────────────┐
│ 修复验证报告 │
├─────────────────────────────────────────────────────────┤
│ 验证时间:24小时 │
│ │
│ 内存使用: │
│ – 最高:65% │
│ – 最低:25% │
│ – 平均:45% │
│ │
│ GC统计: │
│ – Young GC:1250次 │
│ – Full GC:5次 │
│ – GC总时间:3500ms │
│ │
│ 结论:内存泄漏已修复,内存使用正常 │
└─────────────────────────────────────────────────────────┘

风哥提示:内存泄漏修复后需要进行充分的验证测试,包括长时间运行测试和压力测试,确保问题彻底解决。学习交流加群风哥QQ113257174

Part04-生产案例与实战讲解

4.1 缓存导致内存泄漏案例

缓存导致内存泄漏案例:

4.1.1 案例背景

# 缓存导致内存泄漏案例

故障现象:
应用运行一段时间后内存持续增长,最终OOM

诊断过程:
1. 监控内存趋势
时间 堆内存使用率
10:00 30%
12:00 45%
14:00 60%
16:00 75%
18:00 90%
20:00 OOM

2. 生成堆转储分析
使用MAT分析发现:
– FGeduCache占用500MB
– HashMap中存储100,000个对象
– 对象持续增长

3. 分析代码
public class FGeduCache {
private static Map cache = new HashMap<>();

public void put(String key, Object value) {
cache.put(key, value); // 问题:无限增长
}

public Object get(String key) {
return cache.get(key);
}
}

问题原因:
– 缓存没有大小限制
– 缓存没有过期机制
– 对象持续添加,无法回收

解决方案:
public class FGeduCache {
private static final int MAX_SIZE = 10000;
private static final long EXPIRE_TIME = 3600000; // 1小时

private static Cache cache = CacheBuilder.newBuilder()
.maximumSize(MAX_SIZE)
.expireAfterAccess(EXPIRE_TIME, TimeUnit.MILLISECONDS)
.recordStats()
.build();

public void put(String key, Object value) {
cache.put(key, new CacheEntry(value, System.currentTimeMillis()));
}

public Object get(String key) {
CacheEntry entry = cache.getIfPresent(key);
return entry != null ? entry.getValue() : null;
}

public static class CacheEntry {
private Object value;
private long createTime;

// constructor, getters…
}
}

修复效果:
– 内存使用稳定在50%以下
– 缓存自动清理过期数据
– 无内存泄漏

4.2 连接未关闭导致内存泄漏案例

连接未关闭导致内存泄漏案例:

4.2.1 案例背景

# 连接未关闭导致内存泄漏案例

故障现象:
数据库连接池耗尽,应用无法获取连接

诊断过程:
1. 检查连接池状态
/WebSphere/app/profiles/Dmgr01/bin/wsadmin.sh -lang jython \
-username fgeduadmin -password fgedu123 -c ”
ds = AdminControl.queryNames(‘type=DataSource,name=fgedudb,*’)
print AdminControl.getAttribute(ds, ‘stats’)
” 2>/dev/null

连接池状态:
– 总连接数:50
– 使用连接数:50
– 空闲连接数:0
– 等待获取连接的线程:15

2. 分析堆转储
发现大量Connection对象未被回收
引用链:Thread -> FGeduDAO -> Connection

3. 分析代码
public class FGeduDAO {
public List getUsers() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery(“SELECT * FROM fgedu_user”);
List users = new ArrayList<>();
while (rs.next()) {
users.add(mapUser(rs));
}
return users;
} catch (SQLException e) {
throw new RuntimeException(e);
// 问题:异常时连接未关闭
}
// 问题:正常时也未关闭连接
}
}

问题原因:
– 数据库连接未关闭
– 异常处理不完善
– 连接池耗尽

解决方案:
public class FGeduDAO {
public List getUsers() {
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(“SELECT * FROM fgedu_user”)) {

List users = new ArrayList<>();
while (rs.next()) {
users.add(mapUser(rs));
}
return users;
} catch (SQLException e) {
logger.error(“查询用户失败”, e);
throw new RuntimeException(e);
}
}
}

修复效果:
– 连接正常释放
– 连接池使用率正常
– 无连接泄漏

4.3 监听器未注销导致内存泄漏案例

监听器未注销导致内存泄漏案例:

4.3.1 案例背景

# 监听器未注销导致内存泄漏案例

故障现象:
应用热部署后内存持续增长

诊断过程:
1. 监控内存趋势
每次热部署后内存增长约100MB

2. 分析堆转储
发现大量FGeduListener对象
引用链:EventManager -> List -> FGeduListener

3. 分析代码
public class FGeduListener implements EventListener {
public FGeduListener() {
EventManager.getInstance().register(this); // 注册监听器
}

// 问题:没有注销方法
}

问题原因:
– 监听器注册后未注销
– 热部署时旧监听器仍被引用
– 类加载器无法卸载

解决方案:
public class FGeduListener implements EventListener {
private boolean registered = false;

public FGeduListener() {
EventManager.getInstance().register(this);
registered = true;
}

public void destroy() {
if (registered) {
EventManager.getInstance().unregister(this);
registered = false;
}
}
}

// 使用ServletContextListener管理生命周期
public class FGeduContextListener implements ServletContextListener {
private FGeduListener listener;

@Override
public void contextInitialized(ServletContextEvent sce) {
listener = new FGeduListener();
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
if (listener != null) {
listener.destroy(); // 注销监听器
}
}
}

修复效果:
– 热部署后内存正常
– 监听器正确注销
– 类加载器正常卸载

Part05-风哥经验总结与分享

5.1 内存泄漏排查检查清单

内存泄漏排查检查清单:

# 内存泄漏排查检查清单

问题确认:
□ 内存使用是否持续增长
□ GC后内存是否下降
□ Full GC是否频繁
□ 是否有OOM错误

数据收集:
□ 堆转储是否生成
□ GC日志是否收集
□ 应用日志是否收集
□ 线程转储是否生成

数据分析:
□ 是否找到大对象
□ 是否分析引用链
□ 是否确定泄漏类型
□ 是否定位泄漏代码

修复验证:
□ 是否修复代码
□ 是否部署测试
□ 是否压力测试
□ 是否长时间运行测试

# 内存泄漏排查脚本
#!/bin/bash
# leak_check.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn

echo “=== WebSphere内存泄漏排查 ===”

# 1. 检查内存使用
echo “1. 内存使用情况:”
/WebSphere/app/profiles/Dmgr01/bin/wsadmin.sh -lang jython \
-username fgeduadmin -password fgedu123 -c ”
jvm = AdminControl.queryNames(‘type=JVM,process=server1,*’)
heap = AdminControl.getAttribute(jvm, ‘heapMemoryUsage’)
print ‘堆内存使用率:%.2f%%’ % (heap.get(‘used’) * 100.0 / heap.get(‘max’))
” 2>/dev/null

# 2. 检查GC情况
echo “”
echo “2. GC情况:”
grep “Full GC” /WebSphere/logs/gc.log | tail -5

# 3. 生成堆转储
echo “”
echo “3. 是否生成堆转储?(y/n)”
read answer
if [ “$answer” = “y” ]; then
/WebSphere/app/profiles/Dmgr01/bin/wsadmin.sh -lang jython \
-username fgeduadmin -password fgedu123 -c ”
jvm = AdminControl.queryNames(‘type=JVM,process=server1,*’)
AdminControl.invoke(jvm, ‘generateHeapDump’)
” 2>/dev/null
echo “堆转储已生成”
fi

echo “”
echo “=== 排查完成 ===”

5.2 内存泄漏常见问题

内存泄漏常见问题及解决方案:

5.2.1 常见问题汇总

# 内存泄漏常见问题

问题1:堆转储生成失败
原因:磁盘空间不足或权限问题
解决:检查磁盘空间和权限

问题2:堆转储分析工具内存不足
原因:堆转储文件太大
解决:增加工具内存,如 -Xmx4g

问题3:无法定位泄漏对象
原因:对象引用链复杂
解决:使用多个工具交叉分析

问题4:修复后仍有泄漏
原因:存在多个泄漏点
解决:全面排查,逐个修复

问题5:生产环境无法生成堆转储
原因:生成堆转储会暂停应用
解决:选择业务低峰期操作

5.3 内存管理最佳实践

基于多年WebSphere运维经验,总结内存管理最佳实践:

5.3.1 编码最佳实践

  • 资源管理:使用try-with-resources确保资源关闭
  • 缓存管理:设置缓存大小限制和过期时间
  • 监听器管理:成对注册和注销监听器
  • ThreadLocal管理:使用后及时清理

5.3.2 运维最佳实践

  • 监控告警:建立完善的内存监控和告警机制
  • 定期检查:定期分析内存使用趋势
  • 压力测试:上线前进行充分的压力测试
  • 持续优化:根据监控数据持续优化内存配置
生产环境建议:内存泄漏排查需要系统化的方法和工具支持,建议建立完善的监控体系,定期进行内存分析,及时发现和解决内存问题。from WebSphere视频:www.itpux.com

本文档详细介绍了WebSphere 9.0.5的内存泄漏问题排查与解决,包括内存泄漏类型、原因分析、检测方法、堆转储分析、修复方案等内容。通过学习本文档,读者可以掌握WebSphere内存泄漏排查的方法和最佳实践。更多视频教程www.fgedu.net.cn

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息