Fixed memory leak on the client caused by Netty holding references to the World. Closes #415 #417

This commit is contained in:
Lex Manos 2014-05-05 22:14:13 -07:00
parent ad3b232604
commit 5a2b6a604b
3 changed files with 24 additions and 7 deletions

View file

@ -14,6 +14,7 @@ package cpw.mods.fml.client;
import java.io.File;
import java.io.FileInputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -161,7 +162,7 @@ public class FMLClientHandler implements IFMLSidedHandler
private Map<ServerStatusResponse,JsonObject> extraServerListData;
private Map<ServerData, ExtendedServerListData> serverDataTag;
private NetHandlerPlayClient currentPlayClient;
private WeakReference<NetHandlerPlayClient> currentPlayClient;
/**
* Called to start the whole game off
@ -596,7 +597,7 @@ public class FMLClientHandler implements IFMLSidedHandler
@Override
public INetHandler getClientPlayHandler()
{
return this.currentPlayClient;
return this.currentPlayClient == null ? null : this.currentPlayClient.get();
}
@Override
public NetworkManager getClientToServerNetworkManager()
@ -811,7 +812,7 @@ public class FMLClientHandler implements IFMLSidedHandler
public void setPlayClient(NetHandlerPlayClient netHandlerPlayClient)
{
playClientBlock.countDown();
this.currentPlayClient = netHandlerPlayClient;
this.currentPlayClient = new WeakReference(netHandlerPlayClient);
}
@Override

View file

@ -8,6 +8,7 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.util.AttributeKey;
import java.lang.ref.WeakReference;
import java.util.List;
import org.apache.logging.log4j.Level;
import cpw.mods.fml.common.FMLLog;
@ -22,13 +23,13 @@ public abstract class FMLIndexedMessageToMessageCodec<A> extends MessageToMessag
* Make this accessible to subclasses
*/
protected static final AttributeKey<ThreadLocal<FMLProxyPacket>> INBOUNDPACKETTRACKER = new AttributeKey<ThreadLocal<FMLProxyPacket>>("fml:inboundpacket");
public static final AttributeKey<ThreadLocal<WeakReference<FMLProxyPacket>>> INBOUNDPACKETTRACKER = new AttributeKey<ThreadLocal<WeakReference<FMLProxyPacket>>>("fml:inboundpacket");
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception
{
super.handlerAdded(ctx);
ctx.attr(INBOUNDPACKETTRACKER).set(new ThreadLocal<FMLProxyPacket>());
ctx.attr(INBOUNDPACKETTRACKER).set(new ThreadLocal<WeakReference<FMLProxyPacket>>());
}
public FMLIndexedMessageToMessageCodec<A> addDiscriminator(int discriminator, Class<? extends A> type)
@ -49,7 +50,8 @@ public abstract class FMLIndexedMessageToMessageCodec<A> extends MessageToMessag
buffer.writeByte(discriminator);
encodeInto(ctx, msg, buffer);
FMLProxyPacket proxy = new FMLProxyPacket(buffer.copy(), ctx.channel().attr(NetworkRegistry.FML_CHANNEL).get());
FMLProxyPacket old = ctx.attr(INBOUNDPACKETTRACKER).get().get();
WeakReference<FMLProxyPacket> ref = ctx.attr(INBOUNDPACKETTRACKER).get().get();
FMLProxyPacket old = ref == null ? null : ref.get();
if (old != null)
{
proxy.setDispatcher(old.getDispatcher());
@ -71,7 +73,7 @@ public abstract class FMLIndexedMessageToMessageCodec<A> extends MessageToMessag
throw new NullPointerException("Undefined message for discriminator " + discriminator + " in channel " + msg.channel());
}
A newMsg = clazz.newInstance();
ctx.attr(INBOUNDPACKETTRACKER).get().set(msg);
ctx.attr(INBOUNDPACKETTRACKER).get().set(new WeakReference(msg));
decodeInto(ctx, payload.slice(), newMsg);
out.add(newMsg);
}

View file

@ -31,8 +31,10 @@ import net.minecraft.server.management.ServerConfigurationManager;
import net.minecraft.util.ChatComponentText;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.network.FMLIndexedMessageToMessageCodec;
import cpw.mods.fml.common.network.FMLNetworkEvent;
import cpw.mods.fml.common.network.FMLNetworkException;
import cpw.mods.fml.common.network.FMLOutboundHandler;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.internal.FMLMessage;
import cpw.mods.fml.common.network.internal.FMLNetworkHandler;
@ -368,6 +370,7 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler<Packet> imple
{
FMLCommonHandler.instance().bus().post(new FMLNetworkEvent.ServerDisconnectionFromClientEvent(manager));
}
cleanAttributes(ctx);
ctx.disconnect(promise);
}
@ -382,6 +385,7 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler<Packet> imple
{
FMLCommonHandler.instance().bus().post(new FMLNetworkEvent.ServerDisconnectionFromClientEvent(manager));
}
cleanAttributes(ctx);
ctx.close(promise);
}
@ -458,4 +462,14 @@ public class NetworkDispatcher extends SimpleChannelInboundHandler<Packet> imple
super.exceptionCaught(ctx, cause);
}
// if we add any attributes, we should force removal of them here so that
//they do not hold references to the world and causes it to leak.
private void cleanAttributes(ChannelHandlerContext ctx)
{
ctx.channel().attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).remove();
ctx.channel().attr(NetworkRegistry.NET_HANDLER).remove();
ctx.channel().attr(NetworkDispatcher.FML_DISPATCHER).remove();
this.handshakeChannel.attr(FML_DISPATCHER).remove();
this.manager.channel().attr(FML_DISPATCHER).remove();
}
}