我们可以从官方文档中获取更加清晰的认识,总结如下:
-
Netty堆外内存回收算法是基于引用计数的;
-
如果一个发送组件(sending component)企图传递一个基于引用计数对象,通常发送组件不需要回收该对象,而是交给消费组件。
-
如果代码块组件(component)接收基于引用计数的内存对象,且不再使用,该组件应负责引用释放——引用计数会-1。如:
ChannelHandler
就是典型的引用对象接收组件。
官方提供了判断是否需要引用释放的案例:
public ByteBuf a(ByteBuf input) {
input.writeByte(42);
return input;
}
public ByteBuf b(ByteBuf input) {
try {
output = input.alloc().directBuffer(input.readableBytes() + 1);
output.writeBytes(input);
output.writeByte(42);
return output;
} finally {
input.release();
}
}
public void c(ByteBuf input) {
System.out.println(input);
input.release();
}
public void main() {
…
ByteBuf buf = …;
// This will print buf to System.out and destroy it.
c(b(a(buf)));
assert buf.refCnt() == 0;
}
| 事件说明 | 什么对象应该释放? | 什么对象释放成功了? |
| — | — | — |
| 1.main()
createsbuf
| buf
→main()
| |
| 2.main()
callsa()
withbuf
| buf
→a()
| |
| 3.a()
returnsbuf
merely. | buf
→main()
| |
| 4.main()
callsb()
withbuf
| buf
→b()
| |
| 5.b()
returns the copy ofbuf
| buf
→b()
,copy
→main()
| b()
releasesbuf
|
| 6.main()
callsc()
withcopy
| copy
→c()
| |
| 7.c()
swallowscopy
| copy
→c()
| c()
releasescopy
|
翻看netty源码,我们可以找到堆外内存回收的一些入口:
io.netty.buffer.AbstractReferenceCountedByteBuf#release()
。
[](()入队消息对象如何回收?
上面的内容是不是看着有点绕,在Netty中Bytebuf一般用在ChannelHandler中,官方文档也提供了较为详细的案例。
- 入队消息(Inbound messages)触发读取(channelRead()),消费收到消息的handler应该负责调用
release()
方法:
如:
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf buf = (ByteBuf) msg;
try {
…
} finally {
buf.release();
}
}
- 消息解析器也会产生引用计数。
// Assuming your handler is placed next to HttpRequestDecoder
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof HttpRequest) {
HttpRequest req = (HttpRequest) msg;
…
}
if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
try {
…
} finally {
content.release();
}
}
}
以上两种情况,在判别上有难度的话,Netty还提供更加简洁的方式来释放引用:
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
…
} finally {
ReferenceCountUtil.release(msg);
}
}
[](()三、堆外内存监控配置
堆外内存到底有没有被回收,那么,在netty中如何进行监控?Netty中有个对象PlatformDependent.class
,其中的"DIRECT_MEMORY_COUNTER"记录了堆外内存占用的情况,我们可以通过反射获取该对象的值,从而达到监控的目的。
[](()反射监控堆外内存
在Springboot项目中,我们可以定义一个定时任务,实时打印堆外内存的情况到日志中。
import io.netty.util.internal.PlatformDependent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
@Configuration //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling // 2.开启定时任务
@Slf4j
public class SaticScheduleTask {
private static final int _1K = 1024;
private static final String BUSINESS_KEY = “netty-direct-memory”;
private AtomicLong directMemory;
@PostConstruct
public void init(){
Field field = ReflectUtil.getField(PlatformDependent.class,“DIRECT_MEMORY_COUNTER”);
field.setAccessible(true);
try {
directMemory = (AtomicLong) field.get(PlatformDependent.class);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
- 每隔5s统计一下堆外直接内存
*/
@Scheduled(fixedRate = 30000)
public void report(){
int memoryInKb = (int) (directMemory.get()/_1K);
log.info(“{}:{}k”,BUSINESS_KEY,memoryInKb);
}
}
[](()服务器上监控堆外内存
cat location-center-info.log |grep netty-direct
结果如下:
[root@localhost location-center]# cat location-center-info.log |grep netty-direct
[location-center:192.168.5.12:8017] [dce11c40a9bc3f77,dce11c40a9bc3f77] 2021-08-02 09:05:42.332 INFO 29468 [pool-3-thread-1] com.keyou.evm.location.task.SaticScheduleTask netty-direct-memory:32768k
[location-center:192.168.5.12:8017] [f486d613826fab3c,f486d613826fab3c] 2021-08-02 09:06:12.285 INFO 29468 [pool-3-thread-1] com.keyou.evm.location.task.SaticScheduleTask netty-direct-memory:131072k
[location-center:192.168.5.12:8017] [a94a3c3012804430,a94a3c3012804430] 2021-08-02 09:06:42.285 INFO 29468 [pool-3-thread-1] com.keyou.evm.location.task.SaticScheduleTask netty-direct-memory:131072k
[location-center:192.168.5.12:8017] [2c1e844ae37e00e0,2c1e844ae37e00e0] 2021-08-02 09:07:12.285 INFO 29468 [pool-3-thread-1] com.keyou.evm.location.task.SaticScheduleTask netty-direct-memory:131072k
[loca 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 tion-center:192.168.5.12:8017] [67106bcae0fb116d,67106bcae0fb116d] 2021-08-02 09:07:42.285 INFO 29468 [pool-3-thread-1] com.keyou.evm.location.task.SaticScheduleTask netty-direct-memory:131072k
[location-center:192.168.5.12:8017] [b9a673e7118c05d7,b9a673e7118c05d7] 2021-08-02 09:08:12.285 INFO 29468 [pool-3-thread-1] com.keyou.evm.location.task.SaticScheduleTask netty-direct-memory:131072k
[location-center:192.168.5.12:8017] [ddfe5f1003ac4514,ddfe5f1003ac4514] 2021-08-02 09:08:42.285 INFO 29468 [pool-3-thread-1] com.keyou.evm.location.task.SaticScheduleTask netty-direct-memory:131072k
[location-center:192.168.5.12:8017] [cd1fbb7b9c84423d,cd1fbb7b9c84423d] 2021-08-02 09:09:12.285 INFO 29468 [pool-3-thread-1] com.keyou.evm.location.task.SaticScheduleTask netty-direct-memory:131072k
[location-center:192.168.5.12:8017] [fd897a29ec939f7f,fd897a29ec939f7f] 2021-08-02 09:09:42.285 INFO 29468 [pool-3-thread-1] com.keyou.evm.location.task.SaticScheduleTask netty-direct-memory:131072k
[location-center:192.168.5.12:8017] [48893694a422f0ae,48893694a422f0ae] 2021-08-02 09:10:12.285 INFO 29468 [pool-3-thread-1] com.keyou.evm.location.task.SaticScheduleTask netty-direct-memory:131072k
[](()四、其他案例