一、ByteBuf 内存模型
1. ByteBuf 结构概览
java
复制
下载
/**
* ByteBuf 内存层次结构
*/
public class ByteBufMemoryModel {
/**
* ByteBuf 的两种主要类型:
* 1. Heap Buffer(堆缓冲区)
* - 存储在 JVM 堆内存
* - 快速分配和释放
* - 受 GC 管理,有 GC 压力
* - 示例:UnpooledHeapByteBuf
*
* 2. Direct Buffer(直接缓冲区)
* - 存储在堆外内存(Direct Memory)
* - 零拷贝:避免 JVM 堆与 Native 堆之间的复制
* - 不受 GC 管理,需要手动释放
* - 示例:UnpooledDirectByteBuf
*/
/**
* 内存分配器类型:
* 1. Pooled(池化)
* - 重用已分配的内存块
* - 减少内存碎片和分配开销
* - 适合高并发、频繁分配的场景
*
* 2. Unpooled(非池化)
* - 每次分配新的内存
* - 简单但效率较低
* - 适合低频分配场景
*/
/**
* 内存管理层次:
* ┌─────────────────────────────────────┐
* │ Application Layer │
* │ (User Code with ByteBuf) │
* └─────────────────────────────────────┘
* │
* ▼
* ┌─────────────────────────────────────┐
* │ Reference Counting Layer │
* │ (refCnt() retain() release()) │
* └─────────────────────────────────────┘
* │
* ▼
* ┌─────────────────────────────────────┐
* │ Memory Pool Layer (可选) │
* │ (PooledByteBufAllocator) │
* └─────────────────────────────────────┘
* │
* ▼
* ┌─────────────────────────────────────┐
* │ Operating System Memory Layer │
* │ (malloc/free, mmap, etc.) │
* └─────────────────────────────────────┘
*/
}
二、引用计数机制详解
1. 引用计数接口
java
复制
下载
/**
* 引用计数核心接口
*/
public interface ReferenceCounted {
/**
* 获取当前引用计数
*/
int refCnt();
/**
* 增加引用计数
* @return this
*/
ReferenceCounted retain();
/**
* 增加指定数量的引用计数
* @param increment 增加的引用数
* @return this
*/
ReferenceCounted retain(int increment);
/**
* 记录当前对象的访问位置(用于调试)
* @return this
*/
ReferenceCounted touch();
/**
* 记录对象的访问位置并指定提示信息
* @param hint 提示信息
* @return this
*/
ReferenceCounted touch(Object hint);
/**
* 减少引用计数,如果引用计数为0则释放资源
* @return true 如果引用计数为0且资源已释放
*/
boolean release();
/**
* 减少指定数量的引用计数
* @param decrement 减少的引用数
* @return true 如果引用计数为0且资源已释放
*/
boolean release(int decrement);
}
/**
* ByteBuf 引用计数实现示例
*/
public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
private static final AtomicIntegerFieldUpdater<AbstractReferenceCountedByteBuf> refCntUpdater =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
// 使用 volatile 保证可见性
private volatile int refCnt = 1;
@Override
public int refCnt() {
return refCnt;
}
@Override
public ByteBuf retain() {
return retain0(1);
}
@Override
public ByteBuf retain(int increment) {
return retain0(checkPositive(increment, "increment"));
}
private ByteBuf retain0(int increment) {
for (;;) {
int refCnt = this.refCnt;
final int nextCnt = refCnt + increment;
// 确保引用计数不会溢出
if (nextCnt <= increment) {
throw new IllegalReferenceCountException(refCnt, increment);
}
// CAS 更新引用计数
if (refCntUpdater.compareAndSet(this, refCnt, nextCnt)) {
break;
}
}
return this;
}
@Override
public boolean release() {
return release0(1);
}
@Override
public boolean release(int decrement) {
return release0(checkPositive(decrement, "decrement"));
}
private boolean release0(int decrement) {
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();
return true;
}
return false;
}
}
}
/**
* 子类需要实现的资源释放方法
*/
protected abstract void deallocate();
}
2. 引用计数生命周期
java
复制
下载
/**
* ByteBuf 生命周期管理
*/
public class ByteBufLifecycleManager {
/**
* ByteBuf 生命周期状态:
*
* 1. 创建 (Creation)
* ┌── ByteBuf buf = allocator.buffer(1024);
* │ refCnt = 1
* │ └── 创建者持有第一个引用
*
* 2. 传递 (Transfer)
* ├── ByteBuf retained = buf.retain();
* │ refCnt = 2
* │ └── 多个使用者共享引用
*
* ├── ByteBuf duplicate = buf.duplicate();
* │ └── 共享底层数据,引用计数不变
*
* ├── ByteBuf slice = buf.slice();
* │ └── 创建视图,引用计数增加
*
* 3. 使用 (Usage)
* ├── 读取数据: buf.readBytes(dst)
* ├── 写入数据: buf.writeBytes(src)
* ├── 标记/重置: buf.markReaderIndex()
*
* 4. 释放 (Release)
* ├── buf.release(); // 释放一个引用
* ├── refCnt--; // 引用计数减少
* └── if (refCnt == 0) { // 完全释放
* deallocate(); // 实际释放内存
* }
*
* 5. 泄漏检测 (Leak Detection)
* └── 如果 ByteBuf 没有被正确释放
* → 内存泄漏检测器会报告
*/
/**
* 内存泄漏检测示例
*/
public static class LeakDetectionExample {
// 启用泄漏检测(生产环境建议使用 SIMPLE 或 DISABLED)
static {
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
}
public void demonstrateLeak() {
ByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
// 这里会发生内存泄漏:ByteBuf 没有被释放
ByteBuf leakedBuf = allocator.buffer(512);
leakedBuf.writeBytes("Leaked data".getBytes());
// 正确做法:使用 try-finally 确保释放
ByteBuf safeBuf = null;
try {
safeBuf = allocator.buffer(512);
safeBuf.writeBytes("Safe data".getBytes());
// 处理数据...
} finally {
if (safeBuf != null && safeBuf.refCnt() > 0) {
safeBuf.release();
}
}
}
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
三、内存释放最佳实践
1. 标准释放模式
java
复制
下载
/**
* ByteBuf 释放的最佳实践
*/
public class ByteBufReleasePatterns {
/**
* 模式1:try-finally 模式(基础版)
*/
public void pattern1TryFinally() {
ByteBuf buf = null;
try {
buf = ByteBufAllocator.DEFAULT.buffer(1024);
// 使用 buf...
writeData(buf);
sendData(buf);
} finally {
if (buf != null) {
// 确保释放
buf.release();
}
}
}
/**
* 模式2:try-with-resources 模式(推荐)
* 需要自定义实现 AutoCloseable
*/
public void pattern2TryWithResources() {
try (ByteBufResource buf = new ByteBufResource(1024)) {
// 自动管理释放
writeData(buf.getBuf());
sendData(buf.getBuf());
} // 自动调用 close() 释放资源
}
/**
* 包装类实现 AutoCloseable
*/
static class ByteBufResource implements AutoCloseable {
private final ByteBuf buf;
public ByteBufResource(int capacity) {
this.buf = ByteBufAllocator.DEFAULT.buffer(capacity);
}
public ByteBuf getBuf() {
return buf;
}
@Override
public void close() {
if (buf.refCnt() > 0) {
buf.release();
}
}
}
/**
* 模式3:引用计数传播模式
* 适用于需要传递 ByteBuf 的场景
*/
public ByteBuf pattern3ReferencePropagation() {
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(1024);
try {
writeData(buf);
// 增加引用计数,让调用者负责释放
return buf.retain();
} finally {
// 释放当前上下文的引用
buf.release();
}
}
/**
* 模式4:工具类辅助模式
*/
public void pattern4UtilClass() {
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(1024);
// 使用工具类确保释放
ByteBufUtil.releaseLater(buf, b -> {
// 在这里安全使用 buf
writeData(b);
sendData(b);
});
}
/**
* 模式5:组合释放模式
* 释放多个 ByteBuf
*/
public void pattern5CompositeRelease() {
ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer(256);
ByteBuf buf2 = ByteBufAllocator.DEFAULT.buffer(512);
ByteBuf buf3 = ByteBufAllocator.DEFAULT.buffer(1024);
try {
// 使用这些缓冲区...
processMultipleBuffers(buf1, buf2, buf3);
} finally {
// 一次性释放所有
ReferenceCountUtil.release(buf1, buf2, buf3);
}
}
}
/**
* ByteBuf 释放工具类
*/
public class ByteBufUtil {
/**
* 安全释放 ByteBuf
*/
public static void safeRelease(ByteBuf buf) {
if (buf != null && buf.refCnt() > 0) {
buf.release();
}
}
/**
* 安全释放多个 ByteBuf
*/
public static void safeRelease(ByteBuf... buffers) {
if (buffers == null) return;
for (ByteBuf buf : buffers) {
safeRelease(buf);
}
}
/**
* 使用后自动释放的模式
*/
public static <T> T releaseAfter(ByteBuf buf, Function<ByteBuf, T> function) {
try {
return function.apply(buf);
} finally {
safeRelease(buf);
}
}
public static void releaseLater(ByteBuf buf, Consumer<ByteBuf> consumer) {
try {
consumer.accept(buf);
} finally {
safeRelease(buf);
}
}
/**
* 复制数据并释放原缓冲区
*/
public static byte[] copyAndRelease(ByteBuf buf) {
try {
byte[] data = new byte[buf.readableBytes()];
buf.readBytes(data);
return data;
} finally {
safeRelease(buf);
}
}
}
2. Netty Handler 中的内存管理
java
复制
下载
/**
* Netty ChannelHandler 中的 ByteBuf 管理
*/
public class NettyHandlerMemoryManagement {
/**
* 入站处理器示例
*/
public class InboundHandlerExample extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
try {
// 处理数据
processInboundData(buf);
// 传递给下一个处理器
ctx.fireChannelRead(msg);
// 注意:这里不需要 release,因为 fireChannelRead 不会增加引用计数
// 最终会在 pipeline 末尾由 TailContext 释放
} catch (Exception e) {
// 发生异常时需要手动释放
buf.release();
throw e;
}
} else {
ctx.fireChannelRead(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 异常处理
ctx.close();
}
}
/**
* 出站处理器示例
*/
public class OutboundHandlerExample extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
// 添加监听器确保释放
promise.addListener(future -> {
if (!future.isSuccess()) {
// 写入失败,释放缓冲区
buf.release();
}
// 写入成功,Netty 会自动释放
});
// 可以在这里修改数据
ByteBuf transformed = transformData(buf);
// 传递修改后的数据
ctx.write(transformed, promise);
// 释放原始缓冲区(因为我们创建了新的)
buf.release();
} else {
ctx.write(msg, promise);
}
}
private ByteBuf transformData(ByteBuf original) {
// 创建新缓冲区并复制数据
ByteBuf transformed = original.alloc().buffer(original.readableBytes());
try {
// 转换逻辑
byte[] data = new byte[original.readableBytes()];
original.readBytes(data);
// 处理数据...
transformed.writeBytes(data);
return transformed;
} catch (Exception e) {
// 发生异常,释放新缓冲区
transformed.release();
throw e;
}
}
}
/**
* 编解码器中的内存管理
*/
public class CodecExample extends MessageToMessageCodec<ByteBuf, CustomMessage> {
@Override
protected void encode(ChannelHandlerContext ctx, CustomMessage msg, List<Object> out) {
ByteBuf buf = ctx.alloc().buffer();
try {
// 编码逻辑
encodeMessage(msg, buf);
out.add(buf.retain()); // 需要 retain 因为 out 列表会持有引用
} finally {
buf.release(); // 释放当前上下文的引用
}
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) {
try {
// 解码逻辑
CustomMessage decoded = decodeMessage(msg);
out.add(decoded);
// ByteBuf 会在编解码器链末尾自动释放
} catch (Exception e) {
// 解码失败,需要释放
msg.release();
throw e;
}
}
}
}
/**
* Netty 内置的内存管理辅助类
*/
public class NettyReferenceCountUtil {
/**
* 使用 ReferenceCountUtil 的示例
*/
public void demonstrateReferenceCountUtil() {
ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer(1024);
ByteBuf buf2 = null;
try {
// 安全的增加引用
buf2 = ReferenceCountUtil.retain(buf1);
// 安全的释放
ReferenceCountUtil.release(buf1);
// 检查是否是引用计数对象
if (ReferenceCountUtil.isReferenceCounted(buf2)) {
System.out.println("buf2 is reference counted: " + buf2.refCnt());
}
// 安全的转换为特定类型
ByteBuf casted = ReferenceCountUtil.safeCast(buf2, ByteBuf.class, "Expected ByteBuf");
} finally {
ReferenceCountUtil.safeRelease(buf1, buf2);
}
}
/**
* 在 try-catch 中使用的模式
*/
public void patternWithTryCatch() {
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(512);
boolean released = false;
try {
// 使用缓冲区
processBuffer(buf);
// 如果一切正常,在这里释放
released = ReferenceCountUtil.release(buf);
} catch (Throwable t) {
// 发生异常,确保释放
if (!released) {
ReferenceCountUtil.safeRelease(buf);
}
throw t;
}
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
四、池化内存管理
1. 池化分配器实现
java
复制
下载
/**
* 池化内存分配器详解
*/
public class PooledMemoryAllocator {
/**
* PooledByteBufAllocator 的工作原理:
*
* 内存池层次结构:
* ┌─────────────────────────────────────────────┐
* │ PooledByteBufAllocator │
* │ (管理多个 PoolArena) │
* └─────────────────────────────────────────────┘
* ├── PoolArena<byte[]> (Heap Arena)
* │ ├── PoolChunkList (不同使用率的块列表)
* │ │ ├── PoolChunk (16MB 的内存块)
* │ │ │ ├── Page (8KB 的页)
* │ │ │ └── Subpage (小于 8KB 的分配)
* │ │ └── ...
* │ └── ...
* │
* └── PoolArena<ByteBuffer> (Direct Arena)
* └── (类似结构)
*
* 分配策略:
* 1. Tiny: < 512B
* 2. Small: 512B - 8KB
* 3. Normal: 8KB - 16MB
* 4. Huge: > 16MB (不池化)
*/
/**
* 配置池化分配器
*/
public class PooledAllocatorConfig {
public void configureAllocator() {
// 创建自定义配置的分配器
PooledByteBufAllocator allocator = new PooledByteBufAllocator(
true, // 预分配内存
false, // 不使用 direct buffer 的 cleaner
2, // Arena 数量 (通常设置为 CPU 核心数)
2, // Direct Arena 数量
8192, // page 大小 (8KB)
11, // maxOrder: 2^11 * 8KB = 16MB
0, // tinyCacheSize
0, // smallCacheSize
0 // normalCacheSize
);
// 或者使用构建器模式
PooledByteBufAllocator builderAllocator = PooledByteBufAllocator.builder()
.pooledHeap(true)
.pooledDirect(true)
.heapArenas(Runtime.getRuntime().availableProcessors())
.directArenas(Runtime.getRuntime().availableProcessors())
.pageSize(8192)
.maxOrder(11)
.tinyCacheSize(0)
.smallCacheSize(0)
.normalCacheSize(0)
.useCacheForAllThreads(false)
.directMemoryCacheAlignment(0)
.build();
}
}
/**
* 内存池监控和统计
*/
public class PooledMemoryMonitor {
public void monitorPooledAllocator(PooledByteBufAllocator allocator) {
// 获取详细统计信息
PooledByteBufAllocatorMetric metric = allocator.metric();
// 堆内存统计
System.out.println("Heap Arenas: " + metric.numHeapArenas());
System.out.println("Heap Cache Size: " + metric.heapCacheSize());
System.out.println("Heap Active Bytes: " + metric.usedHeapMemory());
System.out.println("Heap Allocated Bytes: " + metric.heapMemoryCounter().get());
// 直接内存统计
System.out.println("Direct Arenas: " + metric.numDirectArenas());
System.out.println("Direct Cache Size: " + metric.directCacheSize());
System.out.println("Direct Active Bytes: " + metric.usedDirectMemory());
System.out.println("Direct Allocated Bytes: " + metric.directMemoryCounter().get());
// 每个 Arena 的详细统计
for (PoolArenaMetric arena : metric.heapArenas()) {
System.out.println("Arena: " + arena);
System.out.println(" Num Thread Caches: " + arena.numThreadCaches());
System.out.println(" Num Allocations: " + arena.numAllocations());
System.out.println(" Num Deallocations: " + arena.numDeallocations());
System.out.println(" Active Allocations: " + arena.numActiveAllocations());
System.out.println(" Active Bytes: " + arena.numActiveBytes());
}
// 线程本地缓存统计
for (PoolThreadCacheMetric cache : metric.directArenas().iterator().next().threadCaches()) {
System.out.println("Thread Cache: " + cache);
System.out.println(" Allocations: " + cache.allocationCount());
System.out.println(" Misses: " + cache.missCount());
System.out.println(" Hits: " + cache.hitCount());
}
}
}
}
/**
* 自定义内存分配策略
*/
public class CustomAllocationStrategy {
/**
* 根据大小选择分配策略
*/
public ByteBuf allocateSmart(int size) {
ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
if (size <= 0) {
throw new IllegalArgumentException("Size must be positive");
}
if (size <= 256) {
// 小对象:使用堆内存,减少 direct memory 压力
return allocator.heapBuffer(size);
} else if (size <= 8192) {
// 中等对象:使用池化 direct buffer
return allocator.directBuffer(size);
} else {
// 大对象:使用非池化,避免占用池内存
return Unpooled.directBuffer(size);
}
}
/**
* 复合缓冲区分配
*/
public ByteBuf allocateComposite(int totalSize, int chunkSize) {
CompositeByteBuf composite = ByteBufAllocator.DEFAULT.compositeBuffer();
try {
int remaining = totalSize;
while (remaining > 0) {
int currentChunk = Math.min(chunkSize, remaining);
ByteBuf chunk = ByteBufAllocator.DEFAULT.directBuffer(currentChunk);
// 写入数据到 chunk...
writeChunkData(chunk, currentChunk);
// 添加到复合缓冲区
composite.addComponent(true, chunk);
remaining -= currentChunk;
}
return composite.retain(); // 返回时需要 retain
} catch (Exception e) {
// 发生异常时释放所有组件
composite.release();
throw e;
}
}
}
五、内存泄漏检测与调试
1. 泄漏检测配置
java
复制
下载
/**
* Netty 内存泄漏检测
*/
public class MemoryLeakDetection {
/**
* 泄漏检测级别:
* 1. DISABLED - 完全禁用(生产环境)
* 2. SIMPLE - 简单采样检测(默认)
* 3. ADVANCED - 详细跟踪,记录最近的访问
* 4. PARANOID - 跟踪所有分配(测试环境)
*/
static {
// 设置泄漏检测级别
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.ADVANCED);
// 设置采样率(仅对 SIMPLE 级别有效)
System.setProperty("io.netty.leakDetection.samplingInterval", "100");
// 设置最大跟踪记录数
System.setProperty("io.netty.leakDetection.maxRecords", "4");
// 启用引用跟踪(用于调试)
System.setProperty("io.netty.leakDetection.trackRefs", "true");
}
/**
* 自定义泄漏检测器
*/
public class CustomLeakDetector extends ResourceLeakDetector<ByteBuf> {
public CustomLeakDetector(Class<?> resourceType, int samplingInterval) {
super(resourceType, samplingInterval);
}
@Override
protected void reportTracedLeak(String resourceType, String records) {
// 自定义泄漏报告
System.err.println("LEAK DETECTED: " + resourceType);
System.err.println("Recent access records:");
System.err.println(records);
// 可以在这里添加额外的日志、监控或告警
sendAlertToMonitoringSystem(resourceType, records);
// 生成堆转储用于分析
if (shouldGenerateHeapDump()) {
generateHeapDump();
}
}
@Override
protected void reportUntracedLeak(String resourceType) {
System.err.println("LEAK DETECTED (untraced): " + resourceType);
}
}
/**
* 泄漏检测工具类
*/
public class LeakDetectionTools {
/**
* 强制 GC 并检查内存泄漏
*/
public void forceGcAndCheckLeaks() {
System.gc();
System.runFinalization();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 检查 direct memory 使用情况
checkDirectMemoryUsage();
}
/**
* 检查 Direct Memory 使用情况
*/
private void checkDirectMemoryUsage() {
try {
Class<?> vmClass = Class.forName("sun.misc.VM");
Field maxDirectMemoryField = vmClass.getDeclaredField("maxDirectMemory");
maxDirectMemoryField.setAccessible(true);
long maxDirectMemory = (Long) maxDirectMemoryField.get(null);
BufferPoolMXBean directBufferPool = ManagementFactory
.getPlatformMXBeans(BufferPoolMXBean.class)
.stream()
.filter(b -> b.getName().equals("direct"))
.findFirst()
.orElse(null);
if (directBufferPool != null) {
long used = directBufferPool.getMemoryUsed();
long total = directBufferPool.getTotalCapacity();
long count = directBufferPool.getCount();
System.out.println("Direct Memory Usage:");
System.out.println(" Used: " + used + " bytes");
System.out.println(" Total Capacity: " + total + " bytes");
System.out.println(" Buffer Count: " + count);
System.out.println(" Max Direct Memory: " + maxDirectMemory + " bytes");
if (used > maxDirectMemory * 0.8) {
System.err.println("WARNING: Direct memory usage > 80%");
}
}
} catch (Exception e) {
System.err.println("Failed to check direct memory: " + e.getMessage());
}
}
/**
* 监控 ByteBuf 分配和释放
*/
public class ByteBufAllocationMonitor extends ByteBufAllocatorMetricProvider {
private final Map<String, AllocationStats> threadStats = new ConcurrentHashMap<>();
public ByteBufAllocationMonitor(ByteBufAllocator allocator) {
super(allocator);
}
public ByteBuf monitorAllocation(ByteBuf buf) {
String threadName = Thread.currentThread().getName();
AllocationStats stats = threadStats.computeIfAbsent(
threadName, k -> new AllocationStats()
);
stats.allocationCount++;
stats.totalAllocated += buf.capacity();
// 包装 ByteBuf 以跟踪释放
return new MonitoredByteBuf(buf, threadName, stats);
}
public void printStats() {
System.out.println("ByteBuf Allocation Statistics:");
threadStats.forEach((thread, stats) -> {
System.out.printf(" Thread: %s%n", thread);
System.out.printf(" Allocations: %d%n", stats.allocationCount);
System.out.printf(" Total Allocated: %,d bytes%n", stats.totalAllocated);
System.out.printf(" Avg Size: %,d bytes%n",
stats.allocationCount > 0 ? stats.totalAllocated / stats.allocationCount : 0);
});
}
static class AllocationStats {
int allocationCount;
long totalAllocated;
int releaseCount;
}
class MonitoredByteBuf extends WrappedByteBuf {
private final String threadName;
private final AllocationStats stats;
private final StackTraceElement[] allocationTrace;
MonitoredByteBuf(ByteBuf buf, String threadName, AllocationStats stats) {
super(buf);
this.threadName = threadName;
this.stats = stats;
this.allocationTrace = Thread.currentThread().getStackTrace();
}
@Override
public boolean release() {
boolean released = super.release();
if (released) {
stats.releaseCount++;
// 检查是否所有分配都已释放
if (stats.allocationCount == stats.releaseCount) {
System.out.printf("Thread %s: All ByteBufs released%n", threadName);
}
}
return released;
}
@Override
public boolean release(int decrement) {
boolean released = super.release(decrement);
if (released) {
stats.releaseCount += decrement;
}
return released;
}
}
}
}
}
2. 常见内存泄漏场景
java
复制
下载
/**
* 常见的内存泄漏场景和解决方案
*/
public class CommonMemoryLeakScenarios {
/**
* 场景1:忘记释放 ByteBuf
*/
public class Scenario1_ForgetToRelease {
// ❌ 错误做法
public void leakExample() {
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(1024);
// 使用 buf...
// 忘记调用 buf.release()
}
// ✅ 正确做法
public void correctExample() {
ByteBuf buf = null;
try {
buf = ByteBufAllocator.DEFAULT.buffer(1024);
// 使用 buf...
} finally {
if (buf != null && buf.refCnt() > 0) {
buf.release();
}
}
}
}
/**
* 场景2:异常路径没有释放
*/
public class Scenario2_ExceptionPath {
// ❌ 错误做法
public void leakOnException() throws Exception {
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(1024);
processBuffer(buf); // 可能抛出异常
buf.release(); // 异常时不会执行到这里
}
// ✅ 正确做法
public void correctOnException() throws Exception {
ByteBuf buf = null;
try {
buf = ByteBufAllocator.DEFAULT.buffer(1024);
processBuffer(buf);
} finally {
if (buf != null && buf.refCnt() > 0) {
buf.release();
}
}
}
}
/**
* 场景3:引用计数不平衡
*/
public class Scenario3_ReferenceCountImbalance {
// ❌ 错误做法:retain/release 不匹配
public ByteBuf imbalanceExample() {
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(1024);
buf.retain(); // 增加引用计数
// 使用 buf...
buf.release(); // 只释放一次,但增加了两次?
return buf; // 调用者需要释放,但引用计数可能已经为0
}
// ✅ 正确做法:保持对称
public ByteBuf correctImbalance() {
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(1024);
try {
// 使用 buf...
return buf.retain(); // 为调用者增加引用
} finally {
buf.release(); // 释放自己的引用
}
}
}
/**
* 场景4:在 Handler 中处理不当
*/
public class Scenario4_HandlerMismanagement extends ChannelInboundHandlerAdapter {
// ❌ 错误做法:自己释放了,但 pipeline 还会尝试释放
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
process(buf);
buf.release(); // 提前释放
// 后续的 handler 会尝试释放已释放的 buf
}
}
// ✅ 正确做法:让 pipeline 管理释放
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
try {
process(buf);
} finally {
// 不释放,让 TailContext 处理
ReferenceCountUtil.retain(buf); // 如果需要保持引用
}
}
ctx.fireChannelRead(msg);
}
}
/**
* 场景5:循环引用
*/
public class Scenario5_CircularReference {
// ❌ 错误做法:对象相互持有 ByteBuf 引用
class LeakyContainer {
private ByteBuf data;
private LeakyContainer next;
public void setNext(LeakyContainer next) {
this.next = next;
if (next != null) {
next.data = this.data.retain(); // 循环引用
}
}
}
// ✅ 正确做法:使用弱引用或独立副本
class SafeContainer {
private ByteBuf data;
private WeakReference<SafeContainer> nextRef;
public void setNext(SafeContainer next) {
this.nextRef = new WeakReference<>(next);
if (next != null) {
// 如果需要共享数据,创建副本而不是增加引用
next.data = Unpooled.copiedBuffer(data);
}
}
}
}
}
/**
* 内存泄漏检测测试
*/
public class MemoryLeakTest {
@Test
public void testNoLeak() {
// 启用详细泄漏检测
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
ByteBufAllocator allocator = new PooledByteBufAllocator(true);
// 测试多次分配和释放
for (int i = 0; i < 1000; i++) {
ByteBuf buf = allocator.buffer(1024);
try {
// 使用缓冲区
buf.writeBytes(new byte[512]);
} finally {
buf.release();
}
}
// 强制 GC 并等待泄漏报告
System.gc();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 如果没有泄漏报告,测试通过
System.out.println("No memory leak detected");
}
@Test(expected = Exception.class)
public void testLeakDetection() throws Exception {
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.PARANOID);
ByteBufAllocator allocator = new PooledByteBufAllocator(true);
// 故意造成泄漏
List<ByteBuf> leakList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
ByteBuf buf = allocator.buffer(1024);
leakList.add(buf); // 不释放,造成泄漏
// 每10次分配强制GC一次
if (i % 10 == 0) {
System.gc();
Thread.sleep(100);
}
}
// 这里应该会看到泄漏报告
System.out.println("Leak test completed - check logs for leak reports");
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
六、性能优化建议
1. 内存分配优化
java
复制
下载
/**
* ByteBuf 性能优化技巧
*/
public class ByteBufPerformanceOptimization {
/**
* 技巧1:使用合适的分配器
*/
public class Tip1_ChooseRightAllocator {
public ByteBufAllocator selectAllocator(ApplicationType type) {
switch (type) {
case HIGH_THROUGHPUT:
// 高吞吐量应用:使用池化分配器
return new PooledByteBufAllocator(
true, // 预分配
false, // 不使用 cleaner
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors(),
8192, // 8KB page
11, // 16MB chunk
0, // 禁用 tiny cache
0, // 禁用 small cache
0 // 禁用 normal cache
);
case LOW_LATENCY:
// 低延迟应用:考虑使用非池化或调整池参数
return new PooledByteBufAllocator(
true,
false,
2, // 减少 arena 数量
2,
8192,
11,
256, // 启用 tiny cache
512, // 启用 small cache
1024 // 启用 normal cache
);
case MEMORY_CONSTRAINED:
// 内存受限环境:使用堆内存
return new PooledByteBufAllocator(
false, // 禁用 direct buffer
false,
1,
0, // 没有 direct arena
4096, // 较小 page
10, // 较小 chunk
0,
0,
0
);
default:
return ByteBufAllocator.DEFAULT;
}
}
enum ApplicationType {
HIGH_THROUGHPUT,
LOW_LATENCY,
MEMORY_CONSTRAINED
}
}
/**
* 技巧2:避免不必要的复制
*/
public class Tip2_AvoidUnnecessaryCopy {
// ❌ 错误做法:不必要的复制
public ByteBuf inefficientCopy(ByteBuf original) {
byte[] array = new byte[original.readableBytes()];
original.readBytes(array);
ByteBuf copy = Unpooled.buffer(array.length);
copy.writeBytes(array); // 额外复制
return copy;
}
// ✅ 正确做法:使用 slice 或 duplicate
public ByteBuf efficientView(ByteBuf original) {
// 创建视图,不复制数据
return original.slice(original.readerIndex(), original.readableBytes())
.retain(); // 需要 retain 因为共享底层数据
}
// ✅ 正确做法:使用 copy 只有当需要独立修改时
public ByteBuf copyWhenNeeded(ByteBuf original) {
// 只有当需要修改而不影响原数据时才复制
ByteBuf copy = original.copy();
// 现在可以安全修改 copy 而不影响 original
return copy;
}
}
/**
* 技巧3:批量操作
*/
public class Tip3_BatchOperations {
// ❌ 错误做法:多次小操作
public void inefficientWrite(ByteBuf buf, byte[] data) {
for (byte b : data) {
buf.writeByte(b); // 每次 writeByte 都有边界检查
}
}
// ✅ 正确做法:批量写入
public void efficientWrite(ByteBuf buf, byte[] data) {
buf.writeBytes(data); // 一次批量写入
}
// ✅ 正确做法:使用 CompositeByteBuf 合并多个 buffer
public ByteBuf mergeBuffers(ByteBuf buf1, ByteBuf buf2, ByteBuf buf3) {
CompositeByteBuf composite = ByteBufAllocator.DEFAULT.compositeBuffer(3);
composite.addComponents(true, buf1.retain(), buf2.retain(), buf3.retain());
// 记得释放原始 buffers
buf1.release();
buf2.release();
buf3.release();
return composite;
}
}
/**
* 技巧4:缓存和重用 ByteBuf
*/
public class Tip4_ReuseByteBuf {
private final ThreadLocal<ByteBuf> threadLocalBuffer =
ThreadLocal.withInitial(() ->
ByteBufAllocator.DEFAULT.directBuffer(4096)
);
// 重用线程本地的 ByteBuf
public void processWithReusedBuffer() {
ByteBuf buf = threadLocalBuffer.get();
buf.clear(); // 重用前先清除
try {
// 使用 buf...
writeDataToBuffer(buf);
// 处理数据...
processBufferContents(buf);
} finally {
// 不清除引用,保持线程本地
// 但需要确保在下一次使用前调用 clear()
}
}
// 对象池模式
public class ByteBufPool {
private final Queue<ByteBuf> pool = new ConcurrentLinkedQueue<>();
private final int bufferSize;
public ByteBufPool(int bufferSize, int initialSize) {
this.bufferSize = bufferSize;
for (int i = 0; i < initialSize; i++) {
pool.offer(ByteBufAllocator.DEFAULT.directBuffer(bufferSize));
}
}
public ByteBuf acquire() {
ByteBuf buf = pool.poll();
if (buf == null) {
buf = ByteBufAllocator.DEFAULT.directBuffer(bufferSize);
}
return buf.clear(); // 返回前清除
}
public void release(ByteBuf buf) {
if (buf.capacity() == bufferSize && buf.refCnt() == 1) {
buf.clear(); // 清除内容以备重用
pool.offer(buf);
} else {
// 大小不匹配或引用计数不为1,直接释放
buf.release();
}
}
}
}
/**
* 技巧5:监控和调优
*/
public class Tip5_MonitorAndTune {
public void monitorAndAdjust() {
PooledByteBufAllocator allocator = (PooledByteBufAllocator) ByteBufAllocator.DEFAULT;
PooledByteBufAllocatorMetric metric = allocator.metric();
// 定期监控
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
System.out.println("=== Memory Pool Metrics ===");
System.out.printf("Heap Used: %d / %d%n",
metric.usedHeapMemory(),
metric.heapMemoryCounter().get());
System.out.printf("Direct Used: %d / %d%n",
metric.usedDirectMemory(),
metric.directMemoryCounter().get());
// 如果使用率持续高于阈值,可以动态调整
double heapUsageRatio = (double) metric.usedHeapMemory() / metric.heapMemoryCounter().get();
double directUsageRatio = (double) metric.usedDirectMemory() / metric.directMemoryCounter().get();
if (heapUsageRatio > 0.9 || directUsageRatio > 0.9) {
System.out.println("WARNING: High memory pool usage");
// 可以考虑触发主动 GC 或调整分配策略
}
}, 1, 60, TimeUnit.SECONDS); // 每分钟监控一次
}
// JVM 参数调优建议
public class JvmTuningSuggestions {
/**
* 推荐的 JVM 参数:
*
* 1. 直接内存限制:
* -XX:MaxDirectMemorySize=512m
*
* 2. 堆内存设置:
* -Xms2g -Xmx2g
*
* 3. GC 调优(G1GC 推荐):
* -XX:+UseG1GC
* -XX:MaxGCPauseMillis=200
* -XX:InitiatingHeapOccupancyPercent=35
*
* 4. Netty 特定参数:
* -Dio.netty.leakDetection.level=simple
* -Dio.netty.allocator.numHeapArenas=4
* -Dio.netty.allocator.numDirectArenas=4
* -Dio.netty.allocator.pageSize=8192
* -Dio.netty.allocator.maxOrder=11
* -Dio.netty.allocator.chunkSize=16777216
*
* 5. 系统参数:
* -Dsun.nio.ch.bugLevel="" // 解决 NIO bug
* -Djava.net.preferIPv4Stack=true
*/
}
}
}
七、总结
核心原则:
-
谁分配,谁负责释放 - 或者明确传递责任
-
引用计数必须平衡 - retain 和 release 要成对出现
-
异常安全 - 确保异常路径也能正确释放
-
使用工具辅助 - 利用 Netty 提供的工具类
最佳实践检查表:
-
使用 try-finally 或 try-with-resources 确保释放
-
在 Handler 中遵循 Netty 的引用计数传播规则
-
避免在多个地方释放同一 ByteBuf
-
使用
ReferenceCountUtil进行安全的引用操作 -
在生产环境启用适当的泄漏检测级别
-
监控内存池使用情况并适当调整
-
对性能敏感场景使用池化分配器
-
避免不必要的缓冲区复制,使用 slice/duplicate
-
对大对象考虑使用非池化分配
调试技巧:
-
使用
PARANOID级别检测内存泄漏 -
检查
ResourceLeakDetector的报告 -
监控 Direct Memory 使用情况
-
使用 JMC 或 VisualVM 分析内存使用
-
定期运行内存泄漏测试用例
通过遵循这些原则和实践,可以有效地管理 Netty 的内存使用,避免内存泄漏,并优化应用程序的性能。