内存优化,减轻GC压力

This commit is contained in:
2026-10-29 13:40:01 +08:00
parent b898e4c5be
commit c0e73bce55
6 changed files with 134 additions and 76 deletions
@@ -118,46 +118,50 @@ public class StreamChannelKLALBPacketLink extends AbstractKLALBPacketLink implem
return true; return true;
} }
// 增加两个 ThreadLocal 静态变量,用于复用 4 字节的长度头
private static final ThreadLocal<ByteBuffer> SIZE_WRITE_BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocate(4));
private static final ThreadLocal<ByteBuffer> SIZE_READ_BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocate(4));
@Override @Override
public void writePacket(ByteBuffer kp) throws IOException { public void writePacket(ByteBuffer kp) throws IOException {
ByteBuffer szeWrite=NetworkPacket.bufferAllocator.allocate(4); // 1. 获取并清空缓存的 4 字节 buffer
szeWrite.limit(4); ByteBuffer szeWrite = SIZE_WRITE_BUFFER.get();
//szeWrite.clear(); szeWrite.clear();
// 2. 写入长度并翻转
szeWrite.putInt(kp.limit()); szeWrite.putInt(kp.limit());
szeWrite.flip(); szeWrite.flip();
KLALBVirtualSocketChannel obj = null;
writableChannel.write(new ByteBuffer[] {szeWrite,kp}); // 3. 发送
writableChannel.write(new ByteBuffer[] {szeWrite, kp});
checkflush(); checkflush();
} }
@Override @Override
public ByteBuffer readPacket() throws IOException { public ByteBuffer readPacket() throws IOException {
// 1. 获取并清空缓存的 4 字节 buffer
ByteBuffer szeRead=NetworkPacket.bufferAllocator.allocate(4); ByteBuffer szeRead = SIZE_READ_BUFFER.get();
timeoutTimer=System.nanoTime();
timerenabled=true;
ByteBuffer kp;
szeRead.clear(); szeRead.clear();
szeRead.limit(4); szeRead.limit(4);
//szeRead.clear();
timeoutTimer = System.nanoTime();
timerenabled = true;
ByteBuffer kp;
try { try {
KNEChannels.readFully(readableChannel,szeRead); // 2. 读取长度头
KNEChannels.readFully(readableChannel, szeRead);
szeRead.flip(); szeRead.flip();
int size=szeRead.getInt(0); int size = szeRead.getInt(0);
kp=NetworkPacket.bufferAllocator.allocate(size);
// 3. 分配真正的数据区(这里如果是高频,建议后续也可以池化,不过现在是按需大小分配)
kp = NetworkPacket.bufferAllocator.allocate(size);
kp.limit(size); kp.limit(size);
KNEChannels.readFully(readableChannel, kp); KNEChannels.readFully(readableChannel, kp);
kp.flip(); kp.flip();
return kp; return kp;
}finally { } finally {
timerenabled=false; timerenabled = false;
} }
} }
@@ -252,14 +252,23 @@ public class NTPv4Protocol implements Closeable, AutoCloseable {
checkLock.unlock(); checkLock.unlock();
} }
} }
// 在类成员变量中定义 ThreadLocal 发送缓冲区
private static final ThreadLocal<ByteBuffer> NTP_TX_BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocate(9000));
private void sendNTPPacket(NTPv4Packet nv4, SocketAddress target) throws IOException { private void sendNTPPacket(NTPv4Packet nv4, SocketAddress target) throws IOException {
ByteBuffer bbf = ByteBuffer.allocate(65535); // 1. 获取并重置缓冲区
ByteBuffer bbf = NTP_TX_BUFFER.get();
bbf.clear(); // 重置 position=0, limit=9000
// 2. 写入数据
nv4.writeToChannel(KNEChannels.newWritableChannel(bbf)); nv4.writeToChannel(KNEChannels.newWritableChannel(bbf));
bbf.flip(); bbf.flip(); // 切换为读模式,limit 会变成实际写入的长度
// 3. 发送
DatagramPacket dp = new DatagramPacket(bbf.array(), bbf.limit()); DatagramPacket dp = new DatagramPacket(bbf.array(), bbf.limit());
dp.setSocketAddress(target); dp.setSocketAddress(target);
dgs.send(dp); dgs.send(dp);
if (showPacket) if (showPacket)
System.out.println("NTPv4 TX:" + target + " " + nv4); System.out.println("NTPv4 TX:" + target + " " + nv4);
} }
@@ -379,15 +388,30 @@ public class NTPv4Protocol implements Closeable, AutoCloseable {
return sends; return sends;
} }
// 1. 在类成员变量中定义 ThreadLocal 缓冲区
private static final ThreadLocal<ByteBuffer> NTP_BUFFER = ThreadLocal.withInitial(() -> ByteBuffer.allocate(9000));
private NTPv4Packet receiveNTPPacket(AtomicReference<InetSocketAddress> isa) throws IOException { private NTPv4Packet receiveNTPPacket(AtomicReference<InetSocketAddress> isa) throws IOException {
ByteBuffer bbf = ByteBuffer.allocate(65535); // 2. 从 ThreadLocal 获取,并重置状态 (position=0, limit=9000)
ByteBuffer bbf = NTP_BUFFER.get();
bbf.clear();
// 3. 构造 DatagramPacket
DatagramPacket dp = new DatagramPacket(bbf.array(), bbf.limit()); DatagramPacket dp = new DatagramPacket(bbf.array(), bbf.limit());
dgs.receive(dp); dgs.receive(dp);
isa.set((InetSocketAddress) dp.getSocketAddress()); isa.set((InetSocketAddress) dp.getSocketAddress());
// 4. 关键:限制 ByteBuffer 的读取范围,防止越界读取历史脏数据
bbf.limit(dp.getLength());
NTPv4Packet nv4 = new NTPv4Packet(context.getClock()); NTPv4Packet nv4 = new NTPv4Packet(context.getClock());
// 5. 将 Channel 的读取操作限制在 dp.getLength() 长度内
nv4.readFromChannel(KNEChannels.newReadableChannel(bbf), dp.getLength()); nv4.readFromChannel(KNEChannels.newReadableChannel(bbf), dp.getLength());
if (showPacket) if (showPacket)
System.out.println("NTPv4 RX:" + isa.get() + " " + nv4); System.out.println("NTPv4 RX:" + isa.get() + " " + nv4);
return nv4; return nv4;
} }
@@ -9,6 +9,7 @@ import java.net.Inet6Address;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.SocketException; import java.net.SocketException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -35,6 +36,7 @@ import org.kne.cloud.network.monitor.MonitorData;
import org.kne.cloud.network.monitor.SpeedAndTrafficAndDelayMonitorDataImpl; import org.kne.cloud.network.monitor.SpeedAndTrafficAndDelayMonitorDataImpl;
import org.kne.cloud.network.monitor.SpeedAndTrafficMonitorData; import org.kne.cloud.network.monitor.SpeedAndTrafficMonitorData;
import org.kne.cloud.network.monitor.SpeedAndTrafficMonitorDataImpl; import org.kne.cloud.network.monitor.SpeedAndTrafficMonitorDataImpl;
import org.kne.io.KNEChannels;
import org.kne.membandboost.MembandBoost; import org.kne.membandboost.MembandBoost;
public class KLALBRoutingProtocol extends Thread{ public class KLALBRoutingProtocol extends Thread{
@@ -211,20 +213,15 @@ public class KLALBRoutingProtocol extends Thread{
while(true) { while(true) {
try { try {
byte[]ca=MembandBoost.allocateUninitializedByteArray(65535); KLALBRoutingProtocolPacket kp=receivePacket();
DatagramPacket dgp=new DatagramPacket(ca, ca.length);
ds.receive(dgp);
//System.out.println(Arrays.toString( Arrays.copyOf( dgp.getData(),dgp.getLength())));
ByteArrayInputStream bi=new ByteArrayInputStream(dgp.getData(),0,dgp.getLength());
KLALBRoutingProtocolPacket kp=KLALBRoutingProtocolPacket.readKLALBPacketFromChannel(Channels.newChannel(bi));
switch(kp.getType()) { switch(kp.getType()) {
case KLALBRoutingProtocolPacket.RINFO_REQ: case KLALBRoutingProtocolPacket.RINFO_REQ:
RouterInfo rifr=netmap.get(router.getLocator().getAddress()); RouterInfo rifr=netmap.get(router.getLocator().getAddress());
if(debug) if(debug)
System.out.println("get Request from:"+dgp.getSocketAddress()); System.out.println("get Request from:"+kp.getFrom());
if(rifr!=null) { if(rifr!=null) {
writePacket( new RouterInfoPacket( rifr,false,-1), dgp.getSocketAddress()); writePacket( new RouterInfoPacket( rifr,false,-1), kp.getFrom());
if(debug) if(debug)
System.out.println("response router info:"+rifr); System.out.println("response router info:"+rifr);
}else { }else {
@@ -246,7 +243,7 @@ public class KLALBRoutingProtocol extends Thread{
netmap.put(rif.getLocator().getAddress(), rif); netmap.put(rif.getLocator().getAddress(), rif);
noticeUpdate(); noticeUpdate();
if(rifp.isFlood()) { if(rifp.isFlood()) {
floodPacket( dgp.getSocketAddress(), rifp); floodPacket( kp.getFrom(), rifp);
} }
@@ -259,7 +256,7 @@ public class KLALBRoutingProtocol extends Thread{
break; break;
case KLALBRoutingProtocolPacket.JSON_DATA: case KLALBRoutingProtocolPacket.JSON_DATA:
JsonDataPacket data=(JsonDataPacket) kp; JsonDataPacket data=(JsonDataPacket) kp;
receivers.forEach((v)->{v.accept(dgp.getSocketAddress(),data);}); receivers.forEach((v)->{v.accept(kp.getFrom(),data);});
break; break;
default: default:
break; break;
@@ -276,7 +273,32 @@ public class KLALBRoutingProtocol extends Thread{
} }
} }
} }
private static final ThreadLocal<ByteBuffer> LSDB_RX_BUFFER =
ThreadLocal.withInitial(() -> ByteBuffer.allocate(65535));
private KLALBRoutingProtocolPacket receivePacket() throws IOException {
// 2. 获取并重置缓冲区
ByteBuffer bbf = LSDB_RX_BUFFER.get();
bbf.clear();
// 3. 构造 DatagramPacket(注意这里要传底层 array 和 capacity)
DatagramPacket dgp = new DatagramPacket(bbf.array(), bbf.capacity());
// 4. 阻塞接收
ds.receive(dgp);
// 5. 限制缓冲区范围到实际收到的长度
bbf.limit(dgp.getLength());
bbf.position(0);
// 6. 直接用 KNEChannels 包装 ByteBuffer,避免 ByteArrayInputStream
KLALBRoutingProtocolPacket kp = KLALBRoutingProtocolPacket.readKLALBPacketFromChannel(
KNEChannels.newReadableChannel(bbf)
);
if(kp!=null)
kp.setFrom(dgp.getSocketAddress());
return kp;
}
private void floodPacket(SocketAddress except,RouterInfoPacket rifp) throws IOException { private void floodPacket(SocketAddress except,RouterInfoPacket rifp) throws IOException {
long asn=rifp.getASN(); long asn=rifp.getASN();
List<Neighbor>ln= router.getNeighbors(); List<Neighbor>ln= router.getNeighbors();
@@ -304,20 +326,34 @@ public class KLALBRoutingProtocol extends Thread{
public void sendJsonPacketToAddress(JsonDataPacket pkt,SocketAddress dest) throws IOException { public void sendJsonPacketToAddress(JsonDataPacket pkt,SocketAddress dest) throws IOException {
writePacket(pkt,dest); writePacket(pkt,dest);
} }
// 1. 在类成员变量处定义一个 64KB 的 ThreadLocal 缓冲区
// 注意:这里依然使用 65535,因为你说 LSDB 同步可能需要这么大
private static final ThreadLocal<ByteBuffer> LSDB_TX_BUFFER =
ThreadLocal.withInitial(() -> ByteBuffer.allocate(65535));
private void writePacket(KLALBRoutingProtocolPacket pkt,SocketAddress dest) throws IOException { private void writePacket(KLALBRoutingProtocolPacket pkt, SocketAddress dest) throws IOException {
ByteArrayOutputStream bos=new ByteArrayOutputStream(65535); // 2. 从 ThreadLocal 获取,并清空状态
KLALBRoutingProtocolPacket.writeKLALBPacketToChannel(Channels.newChannel(bos),pkt); ByteBuffer bbf = LSDB_TX_BUFFER.get();
byte[]to=bos.toByteArray(); bbf.clear();
DatagramPacket dgpx=new DatagramPacket(to,to.length);
// 3. 使用 KNEChannels 绑定到 ByteBuffer,直接写入
// 这避免了 ByteArrayOutputStream 内部的动态扩容和拷贝
KLALBRoutingProtocolPacket.writeKLALBPacketToChannel(KNEChannels.newWritableChannel(bbf), pkt);
// 4. 翻转,准备读取数据
bbf.flip();
// 5. 直接拿底层的 byte[] 和真实的 limit 发送
DatagramPacket dgpx = new DatagramPacket(bbf.array(), bbf.limit());
dgpx.setSocketAddress(dest); dgpx.setSocketAddress(dest);
sendLock.lock(); sendLock.lock();
try { try {
ds.send(dgpx); ds.send(dgpx);
}catch(BindException e) { } catch (BindException e) {
e.printStackTrace(); e.printStackTrace();
System.err.println("Cannot assign:"+dest); System.err.println("Cannot assign:" + dest);
}finally { } finally {
sendLock.unlock(); sendLock.unlock();
} }
} }
@@ -4,6 +4,7 @@ import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel; import java.nio.channels.ReadableByteChannel;
@@ -133,4 +134,11 @@ public abstract class KLALBRoutingProtocolPacket extends NetworkPacket {
public static void writeKLALBPacketToChannel(WritableByteChannel writableByteChannel,KLALBRoutingProtocolPacket klb) throws IOException { public static void writeKLALBPacketToChannel(WritableByteChannel writableByteChannel,KLALBRoutingProtocolPacket klb) throws IOException {
klb.writeToChannel(writableByteChannel); klb.writeToChannel(writableByteChannel);
} }
private transient SocketAddress from;
public void setFrom(SocketAddress from) {
this.from=from;
}
public SocketAddress getFrom(){
return from;
}
} }
+13 -27
View File
@@ -13,6 +13,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -204,34 +205,19 @@ public class SRv6Router {
srListeners.remove(listener); srListeners.remove(listener);
} }
public void sortRouteTabel() { public void sortRouteTabel() {
List<Map<IPv6Address, List<RouteItem>>> routeTabel1x = routeTabel1;
// 构建前缀长度索引的路由表 for (Map<IPv6Address, List<RouteItem>> map : routeTabel1x) {
List<Map<IPv6Address, List<RouteItem>>> routeTabel1x = new ArrayList<Map<IPv6Address, List<RouteItem>>>( // 使用 compute 直接替换
IPv6_BITS + 1); map.replaceAll((key, oldList) -> {
for (int i = 0; i < IPv6_BITS + 1; i++) { if (oldList == null || oldList.isEmpty()) {
routeTabel1x.add(new HashMap<IPv6Address, List<RouteItem>>(1024, 0.5f)); return oldList;
}
List<RouteItem> newList = new ArrayList<>(oldList);
Collections.sort(newList);
return newList;
});
} }
List<RouteItem> routeTabel0x=routeTable0;
// 按前缀长度组织路由表
for (RouteItem item : routeTabel0x) {
IPv6AddressGroup iag = item.getDestination();
Map<IPv6Address, List<RouteItem>> map = routeTabel1x.get(iag.getPrefixLength());
List<RouteItem> newlist = new ArrayList<RouteItem>();
List<RouteItem> oldlist = map.putIfAbsent(iag.getAddress(), newlist);
if (oldlist == null) {
oldlist = newlist;
}
oldlist.add(item);
}
for(Map<IPv6Address, List<RouteItem>> map:routeTabel1x) {
for(List<RouteItem>v:map.values()) {
Collections.sort(v);
}
}
// System.out.println(routeTabel1x);
routeTabel1 = routeTabel1x; // 更新索引路由表
} }
// 更新路由表 // 更新路由表
public void updateRouteTabel() { public void updateRouteTabel() {
@@ -264,7 +250,7 @@ public class SRv6Router {
ArrayList<Map<IPv6Address, List<RouteItem>>> routeTabel1x = new ArrayList<Map<IPv6Address, List<RouteItem>>>( ArrayList<Map<IPv6Address, List<RouteItem>>> routeTabel1x = new ArrayList<Map<IPv6Address, List<RouteItem>>>(
IPv6_BITS + 1); IPv6_BITS + 1);
for (int i = 0; i < IPv6_BITS + 1; i++) { for (int i = 0; i < IPv6_BITS + 1; i++) {
routeTabel1x.add(new HashMap<IPv6Address, List<RouteItem>>(1024, 0.5f)); routeTabel1x.add(new ConcurrentHashMap<IPv6Address, List<RouteItem>>());
} }
// 按前缀长度组织路由表 // 按前缀长度组织路由表