一、引用计数核心原理
1.1 引用计数机制概述
java
复制
下载
/**
* Netty引用计数接口
* 基于引用计数的内存管理机制
*/
public interface ReferenceCounted {
/**
* 返回当前引用计数
*/
int refCnt();
/**
* 增加引用计数
*/
ReferenceCounted retain();
/**
* 增加引用计数(指定增量)
*/
ReferenceCounted retain(int increment);
/**
* 减少引用计数,计数为0时释放内存
*/
boolean release();
/**
* 减少引用计数(指定减量)
*/
boolean release(int decrement);
/**
* 创建一个浅拷贝,不增加引用计数
*/
ReferenceCounted touch();
/**
* 创建一个带提示的浅拷贝
*/
ReferenceCounted touch(Object hint);
}
1.2 ByteBuf的引用计数实现
java
复制
下载
/**
* ByteBuf引用计数实现分析
*/
public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
// 引用计数字段(使用原子操作保证线程安全)
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
// 引用计数(volatile保证可见性)
@SuppressWarnings("FieldMayBeFinal")
private volatile int refCnt = 1; // 初始引用计数为1
// 内存泄漏检测器
private static final ResourceLeakDetector<ByteBuf> leakDetector =
ResourceLeakDetectorFactory.instance().newResourceLeakDetector(ByteBuf.class);
// 泄漏跟踪(用于调试)
private ResourceLeakTracker<ByteBuf> leak;
protected AbstractReferenceCountedByteBuf(int maxCapacity) {
super(maxCapacity);
// 启用内存泄漏检测
if (leakDetector.isEnabled()) {
leak = leakDetector.track(this);
}
}
@Override
public int refCnt() {
return refCnt;
}
@Override
public ByteBuf retain() {
return retain0(1);
}
@Override
public ByteBuf retain(int increment) {
return retain0(ObjectUtil.checkPositive(increment, "increment"));
}
private ByteBuf retain0(int increment) {
// 自旋CAS操作增加引用计数
for (;;) {
int refCnt = this.refCnt;
// 确保引用计数大于0
if (refCnt <= 0) {
throw new IllegalReferenceCountException(refCnt, increment);
}
if (refCnt > Integer.MAX_VALUE - increment) {
throw new IllegalReferenceCountException(refCnt, increment);
}
// CAS更新引用计数
if (refCntUpdater.compareAndSet(this, refCnt, refCnt + increment)) {
break;
}
}
return this;
}
@Override
public boolean release() {
return release0(1);
}
@Override
public boolean release(int decrement) {
return release0(ObjectUtil.checkPositive(decrement, "decrement"));
}
private boolean release0(int decrement) {
// 自旋CAS操作减少引用计数
for (;;) {
int refCnt = this.refCnt;
if (refCnt < decrement) {
throw new IllegalReferenceCountException(refCnt, -decrement);
}
// CAS更新引用计数
if (refCntUpdater.compareAndSet(this, refCnt, refCnt - decrement)) {
if (refCnt == decrement) {
// 引用计数降为0,释放资源
deallocate();
// 关闭泄漏跟踪
if (leak != null) {
leak.close(this);
}
return true;
}
return false;
}
}
}
/**
* 实际释放内存的方法(由子类实现)
*/
protected abstract void deallocate();
/**
* 检查引用计数有效性
*/
protected final void ensureAccessible() {
if (refCnt() == 0) {
throw new IllegalReferenceCountException(0);
}
}
@Override
public ByteBuf touch() {
return touch(null);
}
@Override
public ByteBuf touch(Object hint) {
if (leak != null) {
leak.record(hint);
}
return this;
}
}
二、内存管理实现详解
2.1 堆内存ByteBuf实现
java
复制
下载
/**
* 堆内存ByteBuf实现
*/
public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {
// 底层字节数组
private byte[] array;
// ByteBuffer包装(用于NIO操作)
private ByteBuffer tmpNioBuf;
// 内存分配器
private final ByteBufAllocator alloc;
public UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
super(maxCapacity);
this.alloc = ObjectUtil.checkNotNull(alloc, "alloc");
// 参数检查
if (initialCapacity > maxCapacity) {
throw new IllegalArgumentException(
String.format("initialCapacity(%d) > maxCapacity(%d)",
initialCapacity, maxCapacity));
}
// 分配字节数组
setArray(allocateArray(initialCapacity));
setIndex(0, 0);
}
/**
* 分配字节数组
*/
private byte[] allocateArray(int initialCapacity) {
return new byte[initialCapacity];
}
/**
* 释放内存实现
*/
@Override
protected void deallocate() {
// 释放数组引用,让GC回收
array = null;
tmpNioBuf = null;
// 记录内存释放
if (TRACE_BYTE_BUF_ALLOCATIONS) {
traceMemoryReleased();
}
}
/**
* 容量调整
*/
@Override
public ByteBuf capacity(int newCapacity) {
ensureAccessible();
if (newCapacity < 0 || newCapacity > maxCapacity()) {
throw new IllegalArgumentException("newCapacity: " + newCapacity);
}
int oldCapacity = array.length;
if (newCapacity == oldCapacity) {
return this;
}
// 容量调整
if (newCapacity > oldCapacity) {
// 扩容
byte[] newArray = new byte[newCapacity];
System.arraycopy(array, 0, newArray, 0, array.length);
setArray(newArray);
} else {
// 缩容
if (readerIndex() <= newCapacity && writerIndex() <= newCapacity) {
byte[] newArray = new byte[newCapacity];
int length = Math.min(newCapacity, writerIndex());
System.arraycopy(array, readerIndex(), newArray, 0, length);
setArray(newArray);
} else {
throw new IndexOutOfBoundsException(
String.format("readerIndex(%d) + length(%d) exceeds capacity(%d)",
readerIndex(), readableBytes(), newCapacity));
}
}
return this;
}
/**
* 获取底层数组(不增加引用计数)
*/
@Override
public byte[] array() {
ensureAccessible();
return array;
}
/**
* 是否支持数组访问
*/
@Override
public boolean hasArray() {
return true;
}
/**
* 获取数组偏移量
*/
@Override
public int arrayOffset() {
return 0;
}
/**
* 转换为ByteBuffer(不增加引用计数)
*/
@Override
public ByteBuffer nioBuffer() {
ensureAccessible();
return ByteBuffer.wrap(array, readerIndex(), readableBytes()).slice();
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
2.2 直接内存ByteBuf实现
java
复制
下载
/**
* 直接内存ByteBuf实现(使用ByteBuffer)
*/
public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {
// 直接内存ByteBuffer
private ByteBuffer buffer;
// ByteBuffer容量
private int capacity;
// 临时ByteBuffer(用于NIO操作)
private ByteBuffer tmpNioBuf;
// 内存分配器
private final ByteBufAllocator alloc;
// 是否需要释放(true表示需要调用Cleaner释放)
private final boolean doNotFree;
public UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
super(maxCapacity);
this.alloc = ObjectUtil.checkNotNull(alloc, "alloc");
this.doNotFree = false;
// 参数检查
if (initialCapacity > maxCapacity) {
throw new IllegalArgumentException(
String.format("initialCapacity(%d) > maxCapacity(%d)",
initialCapacity, maxCapacity));
}
// 分配直接内存
setByteBuffer(allocateDirect(initialCapacity));
}
/**
* 分配直接内存
*/
private ByteBuffer allocateDirect(int initialCapacity) {
// 使用DirectByteBuffer分配直接内存
return ByteBuffer.allocateDirect(initialCapacity);
}
/**
* 释放直接内存实现
*/
@Override
protected void deallocate() {
ByteBuffer buffer = this.buffer;
if (buffer == null) {
return;
}
// 清除buffer引用
this.buffer = null;
if (!doNotFree) {
// 释放直接内存
freeDirect(buffer);
}
// 清除临时buffer
tmpNioBuf = null;
// 记录内存释放
if (TRACE_BYTE_BUF_ALLOCATIONS) {
traceMemoryReleased();
}
}
/**
* 释放直接内存
*/
private void freeDirect(ByteBuffer buffer) {
try {
// 方法1:通过Cleaner释放(推荐)
if (PlatformDependent.hasDirectBufferNoCleanerConstructor()) {
PlatformDependent.freeDirectBuffer(buffer);
}
// 方法2:调用Cleaner.clean()
else if (buffer.isDirect()) {
Cleaner cleaner = ((DirectBuffer) buffer).cleaner();
if (cleaner != null) {
cleaner.clean();
}
}
} catch (Throwable t) {
// 忽略异常,避免影响正常流程
if (logger.isWarnEnabled()) {
logger.warn("Failed to release direct buffer", t);
}
}
}
/**
* 内存对齐优化
*/
private ByteBuffer allocateAlignedDirect(int capacity, int alignment) {
// 分配额外空间用于对齐
int extra = alignment - 1;
ByteBuffer buffer = ByteBuffer.allocateDirect(capacity + extra);
// 计算对齐后的地址
long address = PlatformDependent.directBufferAddress(buffer);
long offset = address & (alignment - 1);
if (offset != 0) {
// 调整位置使其对齐
int position = (int) (alignment - offset);
buffer.position(position);
buffer.limit(position + capacity);
buffer = buffer.slice();
}
return buffer;
}
/**
* 获取底层ByteBuffer(不增加引用计数)
*/
@Override
public ByteBuffer internalNioBuffer() {
ensureAccessible();
ByteBuffer tmpNioBuf = this.tmpNioBuf;
if (tmpNioBuf == null) {
this.tmpNioBuf = tmpNioBuf = buffer.duplicate();
}
return tmpNioBuf;
}
/**
* 是否支持数组访问
*/
@Override
public boolean hasArray() {
return false;
}
}
2.3 池化内存管理(PooledByteBuf)
java
复制
下载
/**
* 池化ByteBuf实现(高性能内存池)
*/
public class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
// 内存池句柄
private final PoolChunk<T> chunk;
private final long handle;
private final int offset;
private final int length;
private final int maxLength;
// 内存池
private final PoolThreadCache cache;
// 临时ByteBuffer
private ByteBuffer tmpNioBuf;
// 回收标记
private boolean recycled;
public PooledByteBuf(PoolChunk<T> chunk, long handle, int offset,
int length, int maxLength, PoolThreadCache cache) {
super(maxLength);
this.chunk = chunk;
this.handle = handle;
this.offset = offset;
this.length = length;
this.maxLength = maxLength;
this.cache = cache;
this.recycled = false;
// 设置读写索引
setIndex(0, 0);
// 内存追踪
if (TRACE_BYTE_BUF_ALLOCATIONS) {
traceMemoryAllocated();
}
}
/**
* 池化内存释放实现
*/
@Override
protected void deallocate() {
if (handle >= 0) {
// 标记为已回收
recycled = true;
// 将内存归还到内存池
final long handle = this.handle;
this.chunk.arena.free(this.chunk, handle, maxLength, cache);
// 清理引用
this.chunk = null;
tmpNioBuf = null;
// 记录内存释放
if (TRACE_BYTE_BUF_ALLOCATIONS) {
traceMemoryReleased();
}
}
}
/**
* 重新初始化(用于对象重用)
*/
void reuse(int maxCapacity) {
// 重置状态
maxCapacity(maxCapacity);
resetRefCnt(); // 重置引用计数为1
setIndex(0, 0);
discardedBytes = 0;
// 清除回收标记
recycled = false;
// 重置标记位
markedReaderIndex = markedWriterIndex = 0;
}
/**
* 重置引用计数
*/
private void resetRefCnt() {
// 重置引用计数为1
refCntUpdater.set(this, 1);
// 重新创建泄漏检测
if (leakDetector.isEnabled()) {
leak = leakDetector.track(this);
}
}
/**
* 容量调整(池化内存的特殊处理)
*/
@Override
public ByteBuf capacity(int newCapacity) {
ensureAccessible();
if (newCapacity == length) {
return this;
}
// 检查新容量是否合法
checkNewCapacity(newCapacity);
// 池化内存的容量调整:重新分配
if (newCapacity > length) {
// 需要扩容
if (newCapacity <= maxLength) {
// 在最大容量范围内,可以重用当前内存块
this.length = newCapacity;
return this;
} else {
// 需要重新分配
return reallocate(newCapacity);
}
} else {
// 缩容
if (newCapacity < length) {
this.length = newCapacity;
writerIndex(Math.min(writerIndex(), newCapacity));
readerIndex(Math.min(readerIndex(), newCapacity));
}
return this;
}
}
/**
* 重新分配内存
*/
private ByteBuf reallocate(int newCapacity) {
// 分配新的内存
PooledByteBuf<T> newBuf = chunk.arena.allocate(cache, newCapacity, maxCapacity());
// 复制数据
newBuf.writeBytes(this, readerIndex(), readableBytes());
// 释放旧内存
release();
return newBuf;
}
}
三、内存泄漏检测与预防
3.1 ResourceLeakDetector实现
java
复制
下载
/**
* 资源泄漏检测器
*/
public class ResourceLeakDetector<T> {
// 检测级别
public enum Level {
DISABLED, // 禁用
SIMPLE, // 简单模式(只记录泄漏)
ADVANCED, // 高级模式(记录栈跟踪)
PARANOID // 偏执模式(每次访问都检查)
}
// 默认采样率(每113次分配检查1次)
private static final int DEFAULT_SAMPLING_INTERVAL = 113;
// 泄漏追踪记录
private final ConcurrentMap<DefaultResourceLeak<?>, LeakEntry> allLeaks =
PlatformDependent.newConcurrentHashMap();
// 检测级别
private volatile Level level = Level.SIMPLE;
// 采样间隔
private volatile int samplingInterval = DEFAULT_SAMPLING_INTERVAL;
/**
* 创建泄漏追踪器
*/
public ResourceLeakTracker<T> track(T obj) {
Level level = this.level;
if (level == Level.DISABLED) {
return null;
}
// 采样检查
if (level.ordinal() < Level.PARANOID.ordinal()) {
if ((PlatformDependent.threadLocalRandom().nextInt(samplingInterval)) == 0) {
reportLeak();
return new DefaultResourceLeak<>(obj, allLeaks);
}
return null;
}
// 偏执模式:每次都追踪
return new DefaultResourceLeak<>(obj, allLeaks);
}
/**
* 报告泄漏
*/
private void reportLeak() {
if (!logger.isErrorEnabled()) {
clear();
return;
}
// 检查泄漏
for (Iterator<Map.Entry<DefaultResourceLeak<?>, LeakEntry>> i =
allLeaks.entrySet().iterator(); i.hasNext();) {
Map.Entry<DefaultResourceLeak<?>, LeakEntry> entry = i.next();
DefaultResourceLeak<?> leak = entry.getKey();
LeakEntry leakEntry = entry.getValue();
// 检查是否泄漏(引用计数为0但未释放)
if (leak.dispose()) {
i.remove();
// 记录泄漏信息
if (leakEntry != null) {
String records = leak.toString();
if (reportedLeaks.putIfAbsent(records, Boolean.TRUE) == null) {
if (records.isEmpty()) {
reportUntracedLeak(resourceType);
} else {
reportTracedLeak(resourceType, records);
}
}
}
}
}
}
/**
* 默认泄漏追踪器实现
*/
private static final class DefaultResourceLeak<T>
extends PhantomReference<T> implements ResourceLeakTracker<T> {
// 泄漏记录(用于调试)
private final Deque<String> records = new ArrayDeque<>();
// 创建时间
private final long creationTime;
// 最后访问时间
private volatile long lastAccessTime;
// 泄漏记录条数限制
private static final int MAX_RECORDS = 4;
DefaultResourceLeak(T referent,
ConcurrentMap<DefaultResourceLeak<?>, LeakEntry> allLeaks) {
super(referent, referentQueue);
this.creationTime = lastAccessTime = System.nanoTime();
this.allLeaks = allLeaks;
// 记录创建堆栈(如果启用高级模式)
if (Level.ADVANCED == level) {
records.push(stackTraceToString(Record.BOTTOM));
}
// 添加到全局追踪表
allLeaks.put(this, LeakEntry.INSTANCE);
}
@Override
public void record() {
record0(null);
}
@Override
public void record(Object hint) {
record0(hint);
}
private void record0(Object hint) {
// 更新最后访问时间
lastAccessTime = System.nanoTime();
// 记录访问轨迹(如果启用高级模式)
if (Level.ADVANCED == level) {
String stackTrace = stackTraceToString(Record.BOTTOM);
synchronized (records) {
// 限制记录条数
if (records.size() < MAX_RECORDS) {
if (hint != null) {
records.push(stackTrace + " (hint: " + hint + ")");
} else {
records.push(stackTrace);
}
} else {
// 替换最旧的记录
records.pollLast();
records.push(stackTrace);
}
}
}
}
@Override
public boolean close() {
// 从追踪表中移除
if (allLeaks.remove(this) != null) {
// 清除引用
clear();
return true;
}
return false;
}
/**
* 检查是否泄漏
*/
boolean dispose() {
// 检查是否被GC回收
if (super.get() == null) {
// 已经被GC回收,确认泄漏
allLeaks.remove(this);
clear();
return true;
}
return false;
}
@Override
public String toString() {
if (records.isEmpty()) {
return "";
}
StringBuilder buf = new StringBuilder(4096);
buf.append(String.format(
"%s leak - created at:%n%s%n",
resourceType, stackTraceToString(creationRecord)));
synchronized (records) {
for (String record : records) {
buf.append(String.format("Last access at:%n%s%n", record));
}
}
return buf.toString();
}
}
}
3.2 内存泄漏预防最佳实践
java
复制
下载
/**
* ByteBuf使用最佳实践
*/
public class ByteBufBestPractices {
/**
* 场景1:在ChannelHandler中使用ByteBuf
*/
public class SafeChannelHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
try {
// 处理数据
processData(buf);
} finally {
// 必须释放!
buf.release();
}
} else {
// 传递到下一个handler
ctx.fireChannelRead(msg);
}
}
private void processData(ByteBuf buf) {
// 数据处理逻辑
byte[] data = new byte[buf.readableBytes()];
buf.readBytes(data);
// 注意:readBytes会移动readerIndex,但不会释放buf
// buf仍然需要手动释放
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 异常时也要确保资源释放
logger.error("处理异常", cause);
ctx.close();
}
}
/**
* 场景2:需要保留ByteBuf引用的情况
*/
public class RetainedByteBufHandler extends ChannelInboundHandlerAdapter {
// 用于缓存需要保留的ByteBuf
private final Map<Long, ByteBuf> retainedBuffers = new ConcurrentHashMap<>();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
try {
// 读取消息ID
long messageId = buf.readLong();
// 需要保留buf以供后续处理
ByteBuf retainedBuf = buf.retain(); // 增加引用计数
// 保存引用
retainedBuffers.put(messageId, retainedBuf);
// 异步处理
processAsync(messageId, retainedBuf);
} finally {
// 释放原始buf(retain后的引用由retainedBuffers管理)
buf.release();
}
}
}
private void processAsync(long messageId, ByteBuf buf) {
executorService.submit(() -> {
try {
// 处理数据
processRetainedData(buf);
} finally {
// 处理完成后释放
buf.release();
// 从缓存中移除
retainedBuffers.remove(messageId);
}
});
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
// Channel关闭时释放所有保留的ByteBuf
for (ByteBuf buf : retainedBuffers.values()) {
buf.release();
}
retainedBuffers.clear();
super.channelInactive(ctx);
}
}
/**
* 场景3:使用ByteBufUtil进行安全操作
*/
public class ByteBufUtilExample {
public void safeByteBufOperations() {
ByteBuf buf = Unpooled.buffer(1024);
try {
// 写入数据
buf.writeBytes("Hello".getBytes());
// 安全复制(返回新ByteBuf,需要单独释放)
ByteBuf copied = ByteBufUtil.copy(buf);
try {
// 使用copied...
} finally {
copied.release();
}
// 安全比较
ByteBuf otherBuf = Unpooled.buffer(1024);
try {
otherBuf.writeBytes("Hello".getBytes());
boolean equal = ByteBufUtil.equals(buf, otherBuf);
} finally {
otherBuf.release();
}
// 十六进制转储(用于调试)
String hexDump = ByteBufUtil.hexDump(buf);
System.out.println("Hex dump: " + hexDump);
} finally {
// 确保释放
buf.release();
}
}
}
/**
* 场景4:使用CompositeByteBuf
*/
public class CompositeByteBufExample {
public void handleFragmentedPackets() {
// 创建组合ByteBuf
CompositeByteBuf composite = Unpooled.compositeBuffer();
try {
// 添加多个ByteBuf组件
ByteBuf header = Unpooled.buffer(10);
ByteBuf body = Unpooled.buffer(100);
try {
header.writeBytes("HEADER".getBytes());
body.writeBytes("BODY DATA".getBytes());
// 添加到组合缓冲区
composite.addComponents(true, header, body);
// composite现在拥有header和body的所有权
// 不需要单独释放header和body
// 读取组合数据
byte[] allData = new byte[composite.readableBytes()];
composite.readBytes(allData);
} catch (Exception e) {
// 异常时释放单独分配的ByteBuf
header.release();
body.release();
throw e;
}
} finally {
// 释放组合缓冲区(会自动释放所有组件)
composite.release();
}
}
}
/**
* 场景5:使用ReferenceCountUtil进行安全释放
*/
public class ReferenceCountUtilExample {
public void safeReleasePatterns() {
ByteBuf buf1 = null;
ByteBuf buf2 = null;
ByteBuf buf3 = null;
try {
buf1 = Unpooled.buffer(100);
buf2 = Unpooled.buffer(200);
buf3 = Unpooled.buffer(300);
// 业务逻辑...
} catch (Exception e) {
// 异常处理
logger.error("处理失败", e);
} finally {
// 安全释放(避免NullPointerException)
ReferenceCountUtil.safeRelease(buf1);
ReferenceCountUtil.safeRelease(buf2);
ReferenceCountUtil.safeRelease(buf3);
// 或者批量释放
ReferenceCountUtil.release(buf1, buf2, buf3);
}
}
public void retainAndRelease() {
ByteBuf original = Unpooled.buffer(100);
try {
// 增加引用计数
ByteBuf retained = original.retain();
try {
// 使用retained...
process(retained);
} finally {
// 释放retained的引用
retained.release();
}
// original仍然有效
process(original);
} finally {
// 释放original
original.release();
}
}
}
/**
* 场景6:使用try-with-resources模式(需要自定义实现)
*/
public class AutoReleaseByteBuf implements AutoCloseable {
private final ByteBuf buf;
public AutoReleaseByteBuf(ByteBuf buf) {
this.buf = buf;
}
public ByteBuf get() {
return buf;
}
@Override
public void close() {
if (buf != null && buf.refCnt() > 0) {
buf.release();
}
}
// 使用示例
public void usageExample() {
try (AutoReleaseByteBuf autoBuf = new AutoReleaseByteBuf(Unpooled.buffer(100))) {
ByteBuf buf = autoBuf.get();
buf.writeBytes("data".getBytes());
// 自动释放
}
}
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
3.3 内存泄漏检测配置
java
复制
下载
/**
* 内存泄漏检测配置示例
*/
public class LeakDetectionConfiguration {
/**
* 配置内存泄漏检测
*/
public void configureLeakDetection() {
// 方法1:通过系统属性配置(推荐)
System.setProperty("io.netty.leakDetection.level", "ADVANCED");
System.setProperty("io.netty.leakDetection.targetRecords", "4");
System.setProperty("io.netty.leakDetection.samplingInterval", "113");
// 方法2:通过API配置
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
// 方法3:在启动脚本中配置
// java -Dio.netty.leakDetection.level=ADVANCED -jar app.jar
// 不同级别的含义:
// DISABLED: 完全禁用,性能最好
// SIMPLE: 简单检测,记录泄漏次数(默认)
// ADVANCED: 高级检测,记录堆栈信息
// PARANOID: 每次分配都检测,性能最差
}
/**
* 监控内存泄漏
*/
public void monitorLeaks() {
// 定期检查泄漏报告
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
try {
// 检查是否有泄漏
int leakCount = ResourceLeakDetector.getReportedLeakCount();
if (leakCount > 0) {
logger.warn("检测到 {} 个内存泄漏", leakCount);
// 获取泄漏详情
String leakReport = ResourceLeakDetector.getReportedLeakInfo();
if (leakReport != null && !leakReport.isEmpty()) {
logger.error("泄漏详情:\n{}", leakReport);
}
// 发送报警
sendAlert("MEMORY_LEAK_DETECTED",
String.format("发现%d个内存泄漏", leakCount));
}
// 监控内存使用情况
monitorMemoryUsage();
} catch (Exception e) {
logger.error("泄漏监控异常", e);
}
}, 1, 5, TimeUnit.MINUTES); // 5分钟检查一次
}
/**
* 内存使用监控
*/
private void monitorMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
long maxMemory = runtime.maxMemory();
double usedPercentage = (double) usedMemory / maxMemory * 100;
logger.info("内存使用: {}MB / {}MB ({:.2f}%)",
usedMemory / 1024 / 1024,
maxMemory / 1024 / 1024,
usedPercentage);
// 如果使用率超过阈值,触发GC(生产环境慎用)
if (usedPercentage > 80) {
logger.warn("内存使用率过高,触发GC");
System.gc();
}
}
/**
* 内存泄漏诊断工具
*/
public class MemoryLeakDiagnosticTool {
public void diagnoseLeak(ByteBuf leakedBuf) {
// 1. 检查引用计数
int refCnt = leakedBuf.refCnt();
logger.info("引用计数: {}", refCnt);
// 2. 检查是否已释放
if (refCnt == 0) {
logger.error("ByteBuf已释放,但仍在被使用");
}
// 3. 检查分配堆栈
if (leakedBuf instanceof AdvancedLeakAwareByteBuf) {
String allocationStack = ((AdvancedLeakAwareByteBuf) leakedBuf)
.getAllocationStack();
logger.info("分配堆栈:\n{}", allocationStack);
}
// 4. 检查最近访问
if (leakedBuf instanceof AdvancedLeakAwareByteBuf) {
String accessRecords = ((AdvancedLeakAwareByteBuf) leakedBuf)
.getAccessRecords();
if (accessRecords != null) {
logger.info("最近访问记录:\n{}", accessRecords);
}
}
// 5. 内存分析
analyzeMemoryPattern(leakedBuf);
}
private void analyzeMemoryPattern(ByteBuf buf) {
// 检查内存类型
String memoryType = buf.isDirect() ? "直接内存" : "堆内存";
logger.info("内存类型: {}", memoryType);
// 检查容量
logger.info("容量: {} bytes", buf.capacity());
logger.info("最大容量: {} bytes", buf.maxCapacity());
// 检查读写索引
logger.info("读索引: {}, 写索引: {}",
buf.readerIndex(), buf.writerIndex());
logger.info("可读字节: {}", buf.readableBytes());
logger.info("可写字节: {}", buf.writableBytes());
// 检查是否有底层数组
if (buf.hasArray()) {
logger.info("使用数组支持");
} else if (buf.isDirect()) {
logger.info("使用直接内存");
} else if (buf.isComposite()) {
logger.info("组合ByteBuf,包含 {} 个组件",
((CompositeByteBuf) buf).numComponents());
}
}
/**
* 生成内存泄漏报告
*/
public String generateLeakReport() {
StringBuilder report = new StringBuilder();
report.append("=== 内存泄漏分析报告 ===\n\n");
// 收集JVM内存信息
report.append("JVM内存状态:\n");
Runtime runtime = Runtime.getRuntime();
report.append(String.format(" 最大内存: %.2f MB\n",
runtime.maxMemory() / 1024.0 / 1024.0));
report.append(String.format(" 已分配: %.2f MB\n",
runtime.totalMemory() / 1024.0 / 1024.0));
report.append(String.format(" 已使用: %.2f MB\n",
(runtime.totalMemory() - runtime.freeMemory()) / 1024.0 / 1024.0));
report.append(String.format(" 空闲: %.2f MB\n\n",
runtime.freeMemory() / 1024.0 / 1024.0));
// 收集Netty内存池信息(如果有)
report.append("Netty内存池状态:\n");
try {
// 尝试通过反射获取内存池统计信息
report.append(getPoolStats());
} catch (Exception e) {
report.append(" 无法获取内存池信息\n");
}
// 泄漏检测统计
report.append("\n泄漏检测统计:\n");
report.append(String.format(" 已报告泄漏: %d\n",
ResourceLeakDetector.getReportedLeakCount()));
// 建议
report.append("\n建议:\n");
report.append("1. 检查ByteBuf是否正确释放\n");
report.append("2. 使用try-finally确保释放\n");
report.append("3. 启用ADVANCED级别泄漏检测\n");
report.append("4. 定期监控内存使用情况\n");
return report.toString();
}
}
}
四、高级内存管理策略
4.1 内存池优化配置
java
复制
下载
/**
* 内存池配置优化
*/
public class PooledByteBufAllocatorConfig {
/**
* 高性能内存池配置
*/
public ByteBufAllocator createHighPerformanceAllocator() {
return new PooledByteBufAllocator(
// 预分配:true-启用(减少分配延迟)
true,
// 堆内存Arena数量(通常设为CPU核心数)
Runtime.getRuntime().availableProcessors(),
// 直接内存Arena数量
Runtime.getRuntime().availableProcessors(),
// 页大小(默认8KB)
8192,
// 页最大阶数(决定最大分配大小)
11, // 2^11 * 8KB = 16MB
// 小对象缓存大小
256,
// 普通对象缓存大小
64,
// 使用缓存:true(提高性能)
true,
// 对齐缓存行(避免伪共享)
true
);
}
/**
* 低内存消耗配置
*/
public ByteBufAllocator createLowMemoryAllocator() {
return new PooledByteBufAllocator(
true,
// 减少Arena数量节省内存
Math.max(1, Runtime.getRuntime().availableProcessors() / 2),
Math.max(1, Runtime.getRuntime().availableProcessors() / 2),
// 减小页大小
4096,
// 减小最大阶数限制最大分配
10, // 2^10 * 4KB = 4MB
// 减小缓存大小
128,
32,
// 禁用缓存进一步节省内存
false,
false
);
}
/**
* 直接内存优化配置
*/
public ByteBufAllocator createDirectMemoryOptimizedAllocator() {
return new PooledByteBufAllocator(
true,
// 堆内存分配较少
2,
// 直接内存分配较多
Runtime.getRuntime().availableProcessors(),
8192,
11,
// 直接内存对象缓存较大
512,
128,
true,
// 直接内存对齐很重要
true
);
}
/**
* 监控内存池状态
*/
public void monitorPoolStats(PooledByteBufAllocator allocator) {
ScheduledExecutorService monitor = Executors.newScheduledThreadPool(1);
monitor.scheduleAtFixedRate(() -> {
try {
// 获取内存池统计信息
PooledByteBufAllocator.PoolArenaMetric[] arenas = allocator.directArenas();
for (int i = 0; i < arenas.length; i++) {
PooledByteBufAllocator.PoolArenaMetric arena = arenas[i];
logger.info("Arena {} 统计:", i);
logger.info(" 活动分配: {}", arena.numActiveAllocations());
logger.info(" 活动字节: {} bytes", arena.numActiveBytes());
logger.info(" 块数量: {}", arena.numChunkLists());
// 详细统计
for (PooledByteBufAllocator.PoolChunkListMetric chunkList : arena.chunkLists()) {
logger.info(" 块列表 {}:", chunkList.minUsage(), chunkList.maxUsage());
logger.info(" 块数: {}", chunkList.size());
}
}
// 线程本地缓存统计
PooledByteBufAllocator.PoolThreadCacheMetric cache = allocator.threadCache();
if (cache != null) {
logger.info("线程缓存:");
logger.info(" 小对象缓存大小: {}", cache.smallCacheSize());
logger.info(" 普通对象缓存大小: {}", cache.normalCacheSize());
}
} catch (Exception e) {
logger.error("内存池监控异常", e);
}
}, 1, 5, TimeUnit.MINUTES);
}
}
4.2 零拷贝优化
java
复制
下载
/**
* 零拷贝技术应用
*/
public class ZeroCopyExample {
/**
* 文件传输零拷贝
*/
public class ZeroCopyFileHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FileRegion) {
// 使用FileRegion实现零拷贝文件传输
FileRegion region = (FileRegion) msg;
try {
// 直接传输文件,无需复制到用户空间
ctx.write(region);
ctx.flush();
} finally {
// 必须释放FileRegion
region.release();
}
} else {
// 其他类型消息
ctx.fireChannelRead(msg);
}
}
}
/**
* CompositeByteBuf零拷贝
*/
public void compositeZeroCopy() {
// 创建多个ByteBuf
ByteBuf header = Unpooled.buffer(10);
ByteBuf body = Unpooled.buffer(100);
try {
header.writeBytes("HEADER".getBytes());
body.writeBytes("BODY_DATA".getBytes(StandardCharsets.UTF_8));
// 使用CompositeByteBuf合并,无需数据复制
CompositeByteBuf composite = Unpooled.compositeBuffer();
composite.addComponents(true, header.retain(), body.retain());
try {
// 直接传输composite,不会复制底层数据
sendComposite(composite);
// 可以像普通ByteBuf一样操作
byte[] allData = new byte[composite.readableBytes()];
composite.getBytes(composite.readerIndex(), allData);
} finally {
composite.release();
}
} finally {
header.release();
body.release();
}
}
/**
* 切片零拷贝
*/
public void sliceZeroCopy() {
ByteBuf buffer = Unpooled.buffer(1024);
try {
// 写入数据
buffer.writeBytes("HelloWorld".getBytes());
// 创建切片,共享底层数据
ByteBuf slice1 = buffer.slice(0, 5); // "Hello"
ByteBuf slice2 = buffer.slice(5, 5); // "World"
// 切片不会复制数据,与原始buffer共享底层存储
// 需要单独管理引用计数
try {
// 使用slice1和slice2...
processSlice(slice1.retain());
processSlice(slice2.retain());
} finally {
slice1.release();
slice2.release();
}
} finally {
buffer.release();
}
}
/**
* 重复零拷贝
*/
public void duplicateZeroCopy() {
ByteBuf original = Unpooled.buffer(100);
try {
original.writeBytes("OriginalData".getBytes());
// 创建重复视图,共享底层数据
ByteBuf duplicate = original.duplicate();
try {
// duplicate有自己的读写索引,但共享数据
duplicate.readerIndex(0);
// 可以独立操作
duplicate.writeBytes("MoreData".getBytes());
// 注意:修改会影响original
} finally {
duplicate.release();
}
} finally {
original.release();
}
}
}
4.3 内存分配策略优化
java
复制
下载
/**
* 智能内存分配策略
*/
public class SmartByteBufAllocator {
/**
* 自适应分配器
*/
public class AdaptiveByteBufAllocator implements ByteBufAllocator {
private final ByteBufAllocator delegate;
private final AdaptiveRecorder recorder;
// 历史记录大小
private static final int HISTORY_SIZE = 10;
public AdaptiveByteBufAllocator(ByteBufAllocator delegate) {
this.delegate = delegate;
this.recorder = new AdaptiveRecorder(HISTORY_SIZE);
}
@Override
public ByteBuf buffer() {
// 根据历史记录智能选择初始大小
int suggestedSize = recorder.suggestInitialSize();
return buffer(suggestedSize);
}
@Override
public ByteBuf buffer(int initialCapacity) {
ByteBuf buf = delegate.buffer(initialCapacity);
// 记录分配
recorder.recordAllocation(initialCapacity, buf.capacity());
return new TrackedByteBuf(buf, recorder);
}
@Override
public ByteBuf buffer(int initialCapacity, int maxCapacity) {
ByteBuf buf = delegate.buffer(initialCapacity, maxCapacity);
recorder.recordAllocation(initialCapacity, buf.capacity());
return new TrackedByteBuf(buf, recorder);
}
@Override
public ByteBuf ioBuffer() {
return delegate.ioBuffer();
}
@Override
public ByteBuf ioBuffer(int initialCapacity) {
return delegate.ioBuffer(initialCapacity);
}
@Override
public ByteBuf ioBuffer(int initialCapacity, int maxCapacity) {
return delegate.ioBuffer(initialCapacity, maxCapacity);
}
@Override
public ByteBuf heapBuffer() {
return delegate.heapBuffer();
}
@Override
public ByteBuf heapBuffer(int initialCapacity) {
return delegate.heapBuffer(initialCapacity);
}
@Override
public ByteBuf heapBuffer(int initialCapacity, int maxCapacity) {
return delegate.heapBuffer(initialCapacity, maxCapacity);
}
@Override
public ByteBuf directBuffer() {
return delegate.directBuffer();
}
@Override
public ByteBuf directBuffer(int initialCapacity) {
return delegate.directBuffer(initialCapacity);
}
@Override
public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
return delegate.directBuffer(initialCapacity, maxCapacity);
}
@Override
public CompositeByteBuf compositeBuffer() {
return delegate.compositeBuffer();
}
@Override
public CompositeByteBuf compositeBuffer(int maxNumComponents) {
return delegate.compositeBuffer(maxNumComponents);
}
@Override
public CompositeByteBuf compositeHeapBuffer() {
return delegate.compositeHeapBuffer();
}
@Override
public CompositeByteBuf compositeHeapBuffer(int maxNumComponents) {
return delegate.compositeHeapBuffer(maxNumComponents);
}
@Override
public CompositeByteBuf compositeDirectBuffer() {
return delegate.compositeDirectBuffer();
}
@Override
public CompositeByteBuf compositeDirectBuffer(int maxNumComponents) {
return delegate.compositeDirectBuffer(maxNumComponents);
}
@Override
public boolean isDirectBufferPooled() {
return delegate.isDirectBufferPooled();
}
@Override
public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
return delegate.calculateNewCapacity(minNewCapacity, maxCapacity);
}
/**
* 追踪的ByteBuf
*/
private class TrackedByteBuf extends WrappedByteBuf {
private final AdaptiveRecorder recorder;
private final int initialRequestedSize;
TrackedByteBuf(ByteBuf buf, AdaptiveRecorder recorder) {
super(buf);
this.recorder = recorder;
this.initialRequestedSize = buf.capacity();
}
@Override
public ByteBuf capacity(int newCapacity) {
// 记录容量调整
recorder.recordResize(initialRequestedSize, newCapacity);
return super.capacity(newCapacity);
}
@Override
public boolean release() {
boolean released = super.release();
if (released) {
// 记录实际使用情况
int actualUsed = writerIndex();
recorder.recordUsage(initialRequestedSize, actualUsed);
}
return released;
}
}
}
/**
* 自适应记录器
*/
private static class AdaptiveRecorder {
private final int historySize;
private final Queue<AllocationRecord> history;
// 统计信息
private long totalAllocations;
private long totalRequested;
private long totalActual;
AdaptiveRecorder(int historySize) {
this.historySize = historySize;
this.history = new ArrayDeque<>(historySize);
}
void recordAllocation(int requested, int actual) {
AllocationRecord record = new AllocationRecord(requested, actual);
synchronized (history) {
history.offer(record);
if (history.size() > historySize) {
history.poll();
}
totalAllocations++;
totalRequested += requested;
totalActual += actual;
}
}
void recordResize(int oldSize, int newSize) {
// 记录调整大小
synchronized (history) {
totalRequested += (newSize - oldSize);
}
}
void recordUsage(int allocated, int used) {
// 记录实际使用情况
// 可用于优化分配策略
}
int suggestInitialSize() {
synchronized (history) {
if (history.isEmpty()) {
return 1024; // 默认大小
}
// 计算平均使用率
double avgUsage = calculateAverageUsage();
// 基于历史建议大小
int suggested = (int) (calculateAverageRequested() * avgUsage);
// 向上取整到最近的2的幂(内存池友好)
return roundToPowerOfTwo(suggested);
}
}
private double calculateAverageUsage() {
if (totalAllocations == 0) return 1.0;
// 计算平均使用率(避免浪费)
return Math.min(1.0, (double) totalRequested / totalActual);
}
private int calculateAverageRequested() {
if (history.isEmpty()) return 1024;
int sum = 0;
for (AllocationRecord record : history) {
sum += record.requested;
}
return sum / history.size();
}
private int roundToPowerOfTwo(int value) {
// 找到大于等于value的最小的2的幂
int power = 1;
while (power < value) {
power <<= 1;
}
return Math.min(power, 64 * 1024); // 限制最大64KB
}
private static class AllocationRecord {
final int requested;
final int actual;
AllocationRecord(int requested, int actual) {
this.requested = requested;
this.actual = actual;
}
}
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
五、生产环境最佳实践
java
复制
下载
/**
* 生产环境ByteBuf管理最佳实践
*/
public class ProductionByteBufPractices {
/**
* 配置示例:生产环境Netty配置
*/
public class ProductionNettyConfig {
public ServerBootstrap configureServer() {
ServerBootstrap bootstrap = new ServerBootstrap();
// 1. 配置EventLoopGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
// 2. 配置内存分配器
ByteBufAllocator allocator = new PooledByteBufAllocator(
true, // 预分配
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors(),
8192, // 页大小
11, // 最大阶数
256, // 小缓存
64, // 普通缓存
true // 使用缓存
);
// 3. 配置ServerBootstrap
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.ALLOCATOR, allocator)
.option(ChannelOption.RCVBUF_ALLOCATOR,
new AdaptiveRecvByteBufAllocator(64, 1024, 65536))
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.ALLOCATOR, allocator)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// 添加业务处理器
pipeline.addLast(new ByteBufValidator()); // ByteBuf验证
pipeline.addLast(new SafeFrameDecoder()); // 安全解码器
pipeline.addLast(new BusinessHandler()); // 业务处理器
pipeline.addLast(new ExceptionHandler()); // 异常处理器
}
});
// 4. 配置内存泄漏检测
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.SIMPLE);
// 5. 配置关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}));
return bootstrap;
}
}
/**
* ByteBuf验证器(确保正确释放)
*/
public class ByteBufValidator extends ChannelInboundHandlerAdapter {
private final AtomicInteger activeBuffers = new AtomicInteger(0);
private final int maxActiveBuffers = 1000;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
// 检查引用计数
if (buf.refCnt() <= 0) {
logger.error("收到引用计数为0的ByteBuf,丢弃消息");
buf.release();
return;
}
// 限制活跃Buffer数量
if (activeBuffers.incrementAndGet() > maxActiveBuffers) {
logger.warn("活跃ByteBuf数量超限: {}", activeBuffers.get());
buf.release();
activeBuffers.decrementAndGet();
return;
}
try {
// 传递消息
ctx.fireChannelRead(msg);
} finally {
// 确保释放
if (buf.refCnt() > 0) {
buf.release();
activeBuffers.decrementAndGet();
}
}
} else {
ctx.fireChannelRead(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.error("ByteBufValidator异常", cause);
ctx.close();
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// 清理残留的ByteBuf
int remaining = activeBuffers.get();
if (remaining > 0) {
logger.warn("Channel关闭,仍有 {} 个ByteBuf未释放", remaining);
}
super.channelInactive(ctx);
}
}
/**
* 安全解码器
*/
public class SafeFrameDecoder extends ByteToMessageDecoder {
private static final int MAX_FRAME_SIZE = 10 * 1024 * 1024; // 10MB
private static final int MAX_CUMULATIVE_SIZE = 100 * 1024 * 1024; // 100MB
private int cumulativeSize = 0;
private long lastResetTime = System.currentTimeMillis();
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
// 检查累积大小
if (cumulativeSize > MAX_CUMULATIVE_SIZE) {
logger.error("累积数据大小超限: {}", cumulativeSize);
in.skipBytes(in.readableBytes());
ctx.close();
return;
}
// 定期重置累积大小
long now = System.currentTimeMillis();
if (now - lastResetTime > 60000) { // 每分钟重置
cumulativeSize = 0;
lastResetTime = now;
}
// 解码逻辑
while (in.readableBytes() >= 4) {
in.markReaderIndex();
int length = in.readInt();
if (length < 0 || length > MAX_FRAME_SIZE) {
logger.error("非法帧大小: {}", length);
in.resetReaderIndex();
in.skipBytes(1); // 跳过1字节继续尝试
continue;
}
if (in.readableBytes() < length) {
in.resetReaderIndex();
break; // 等待更多数据
}
// 提取帧
ByteBuf frame = in.readRetainedSlice(length);
cumulativeSize += length;
out.add(frame);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.error("解码器异常", cause);
ctx.close();
}
}
/**
* 内存监控服务
*/
public class MemoryMonitorService {
private final ScheduledExecutorService scheduler;
private final List<MemoryMetricsListener> listeners;
private final Map<String, MemoryMetrics> metricsHistory;
public MemoryMonitorService() {
this.scheduler = Executors.newScheduledThreadPool(1);
this.listeners = new CopyOnWriteArrayList<>();
this.metricsHistory = new LinkedHashMap<String, MemoryMetrics>() {
@Override
protected boolean removeEldestEntry(Map.Entry<String, MemoryMetrics> eldest) {
return size() > 1000; // 保留最近1000条记录
}
};
}
public void start() {
scheduler.scheduleAtFixedRate(() -> {
try {
collectAndReportMetrics();
} catch (Exception e) {
logger.error("内存监控异常", e);
}
}, 1, 5, TimeUnit.SECONDS); // 每5秒收集一次
}
private void collectAndReportMetrics() {
MemoryMetrics metrics = collectMetrics();
// 存储历史
String timestamp = LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
metricsHistory.put(timestamp, metrics);
// 通知监听器
for (MemoryMetricsListener listener : listeners) {
try {
listener.onMetricsCollected(metrics);
} catch (Exception e) {
logger.error("监听器异常", e);
}
}
// 检查阈值
checkThresholds(metrics);
// 记录日志
if (logger.isDebugEnabled()) {
logger.debug("内存指标: {}", metrics);
}
}
private MemoryMetrics collectMetrics() {
MemoryMetrics metrics = new MemoryMetrics();
// JVM内存
Runtime runtime = Runtime.getRuntime();
metrics.setJvmTotal(runtime.totalMemory());
metrics.setJvmUsed(runtime.totalMemory() - runtime.freeMemory());
metrics.setJvmMax(runtime.maxMemory());
// 直接内存(通过反射获取)
try {
Class<?> vmClass = Class.forName("sun.misc.VM");
Field maxDirectMemoryField = vmClass.getDeclaredField("maxDirectMemory");
maxDirectMemoryField.setAccessible(true);
metrics.setDirectMax((Long) maxDirectMemoryField.get(null));
// 已使用直接内存(估算)
BufferPoolMXBean directBufferPool = getDirectBufferPool();
if (directBufferPool != null) {
metrics.setDirectUsed(directBufferPool.getMemoryUsed());
metrics.setDirectCount(directBufferPool.getCount());
}
} catch (Exception e) {
logger.warn("无法获取直接内存信息", e);
}
// Netty内存池统计
metrics.setPooledAllocations(getPooledAllocations());
metrics.setPooledActive(getPooledActive());
return metrics;
}
private void checkThresholds(MemoryMetrics metrics) {
// JVM内存使用率
double jvmUsageRatio = (double) metrics.getJvmUsed() / metrics.getJvmMax();
if (jvmUsageRatio > 0.85) {
logger.warn("JVM内存使用率过高: {:.2f}%", jvmUsageRatio * 100);
notifyAlert("JVM_MEMORY_HIGH",
String.format("使用率 %.1f%%", jvmUsageRatio * 100));
}
// 直接内存使用率
if (metrics.getDirectMax() > 0) {
double directUsageRatio = (double) metrics.getDirectUsed() / metrics.getDirectMax();
if (directUsageRatio > 0.8) {
logger.warn("直接内存使用率过高: {:.2f}%", directUsageRatio * 100);
notifyAlert("DIRECT_MEMORY_HIGH",
String.format("使用率 %.1f%%", directUsageRatio * 100));
}
}
// 内存泄漏检测
int leakCount = ResourceLeakDetector.getReportedLeakCount();
if (leakCount > 0) {
logger.error("检测到内存泄漏: {} 个", leakCount);
notifyAlert("MEMORY_LEAK_DETECTED",
String.format("%d 个泄漏", leakCount));
}
}
public void addListener(MemoryMetricsListener listener) {
listeners.add(listener);
}
public Map<String, MemoryMetrics> getHistory() {
return Collections.unmodifiableMap(metricsHistory);
}
public void shutdown() {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
@Data
public static class MemoryMetrics {
private long jvmTotal;
private long jvmUsed;
private long jvmMax;
private long directUsed;
private long directMax;
private long directCount;
private long pooledAllocations;
private long pooledActive;
private long timestamp = System.currentTimeMillis();
}
public interface MemoryMetricsListener {
void onMetricsCollected(MemoryMetrics metrics);
}
/**
* ByteBuf使用检查清单
*/
public static class ByteBufChecklist {
public static List<String> getPreProductionChecks() {
return Arrays.asList(
"□ 1. 是否配置了合适的内存分配器(PooledByteBufAllocator)",
"□ 2. 是否启用了内存泄漏检测(io.netty.leakDetection.level)",
"□ 3. ByteBuf是否正确释放(try-finally或ReferenceCountUtil)",
"□ 4. 是否避免在业务逻辑中直接操作ByteBuf",
"□ 5. 是否设置了合理的消息大小限制",
"□ 6. 是否配置了接收缓冲区分配器(RecvByteBufAllocator)",
"□ 7. 是否处理了ByteBuf释放异常",
"□ 8. 是否监控了内存使用情况",
"□ 9. 是否实现了优雅关闭(释放所有ByteBuf)",
"□ 10. 是否进行了内存泄漏测试"
);
}
public static List<String> getTroubleshootingSteps() {
return Arrays.asList(
"1. 检查内存泄漏检测日志",
"2. 使用jmap/jcmd分析内存使用",
"3. 检查ByteBuf.refCnt()调用位置",
"4. 验证try-finally块是否正确覆盖所有路径",
"5. 检查是否有ByteBuf被意外保留(如放入集合未清理)",
"6. 分析内存使用模式(堆内存 vs 直接内存)",
"7. 检查是否有消息积压导致内存增长",
"8. 验证内存池配置是否合理",
"9. 检查GC日志,确认Full GC频率",
"10. 使用Netty自带的内存监控工具"
);
}
}
}
/**
* 总结
*
* Netty的ByteBuf引用计数和内存释放机制是高性能网络编程的核心。
* 正确理解和应用这些机制可以避免内存泄漏,提高系统稳定性。
*
* 关键原则:
* 1. 谁最后使用,谁负责释放
* 2. 使用try-finally确保释放
* 3. 启用内存泄漏检测
* 4. 监控内存使用情况
* 5. 使用池化内存分配器
*
* 通过本文的详细分析和示例代码,希望能够帮助开发者
* 深入理解Netty内存管理机制,构建高性能、稳定的网络应用。
*/