Java程序的一些优化心得

##JVM参数

  • 高并发使用UseParallelOldGC, -XX:+UseParallelOldGC -XX:-UseAdaptiveSizePolicy

  • 大吞吐量使用UseConcMarkSweepGC, -XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=65

  • -Xms不能太小, -Xms4g -Xmx5g

##开发中的一些心得

  • 进行并发开发时,多线程不是最好的选择,我把多线程换成了Disruptor框架http://lmax-exchange.github.io/disruptor/

  • 在Disruptor中,尽量减少IO操作,每个Consumer是单线程的,不要让线程Block;为提高吞吐量,任何线程的Block都是不可接收的;能缓存的数据一定要缓存,Redis/Memcache都是不错的选择;如果追求更高的性能,一些数据直接缓存进进程内,去Redis里面读数据都是一种延迟。

  • 批量执行的速度会远远超过一个一个的执行,比如从kafka读取数据,或往kafka写数据,比如执行结果需要保存到数据库,如果数据量超过并且数据访问比较频繁,则可以考虑批量保存。将结果保存到MQ中,再将结果异步保存到MySQL或Redis,可以大大提高性能。

  • 单线程比想象中的要快很多,前提是没有Block动作

##使用Netty过程中的一些总结

  • Linux下,为追求性能,可以使用epoll,即Native Transports。EpollEventLoopGroup替换NioEventLoopGroup,EpollEventLoop替换NioEventLoop,EpollServerSocketChannel替换NioServerSocketChannel,EpollSocketChannel替换NioSocketChannel
EventLoopGroup bossGroup = new EpollEventLoopGroup(this.bossThreds);
EventLoopGroup workerGroup = new EpollEventLoopGroup(this.workerThreads);

try {


    ServerBootstrap b = new ServerBootstrap();
    b.group(bossGroup, workerGroup).channel(EpollServerSocketChannel.class).childHandler(serverInitializer);

    b.childOption(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 32 * 1024);
    b.childOption(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 8 * 1024);

    b.option(ChannelOption.TCP_NODELAY, true);
    b.option(ChannelOption.SO_KEEPALIVE, true);
    b.option(ChannelOption.SO_REUSEADDR, true);
    b.option(ChannelOption.SO_RCVBUF, 1048576);
    b.option(ChannelOption.SO_SNDBUF, 1048576);
    b.option(ChannelOption.SO_BACKLOG, 5000);
    b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);


    b.bind("0.0.0.0", port).sync().channel().closeFuture().sync();
} catch (InterruptedException e) {
    logger.error(e.getMessage(), e);
    // e.printStackTrace();
} finally {
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}
  • 开启PooledByteBufAllocator

  • 修改TCP参数TCP_NO_DELAY,SO_SNDBUF,SO_RCVBUF

  • 为获得更大吞吐量,在ChannelHandler的channelRead中,不要Block线程,执行越快越好

  • 在EventLoop中慎用java.util.concurrent中的一些Block性质的操作;数据库操作相当于Block操作,应谨慎使用

  • 使用ctx.writeAndFlush(msg); 而不是ctx.channel().writeAndFlush(msg);

  • 如果业务无状态,可以在ChannelHandler上使用@ChannelHandler.Sharable,可以减少GC

    1
    2
    @ChannelHandler.Sharable
    public class StatelessHandler extends ChannelInboundHandlerAdapter {
  • 在encode时,减少内存copy,尽量使用系统的ByteBuf

public class EncodeActsOnByteArray extends MessageToByteEncoder<YourMessage> {
    public EncodeActsOnByteArray() { super(false); }
    @Override
    public encode(ChannelHandlerContext ctx, YourMessage msg, ByteBuf out) {
      byte[] array = out.array();
      int offset = out.arrayOffset() + out.writerIndex();
      out.writeIndex(out.writerIndex() + encode(msg, array, offset));
    }
    private int encode(YourMessage msg, byte[] array, int offset, int len) { ... }
}