前因: 因为测试 长时间压测导致 接口反应越来越慢,甚至 导致服务器 崩溃
排查过程
1、top 查看是 哪个进程 占用 内存过高
2、根据 进程 id 去查找 具体是哪个 程序的问题
ps -ef| grep 41356 可以看到 具体的 容器位置
排查该进程 对象存活 状态,(如果有异常 大量自建对象创建的话,也能初步定位到问题)
jmap -histo:live 41356 | more
当然 我们也可以 根据条件 进行过滤 jmap -histo:live 41356 | grep 'com.XX'
亦 或者将 其存储到 文本中 进行查看 jmap -histo:live 41356 >a.log
3、生成 dump 文件,使用 jprofiler 或者 eclipse的 Mat插件 进行分析
jmap -dump:live,format=b,file=heap.hprof 41356
4、我这边是因为有个同事 使用 netty框架 , 然后没有 开启 bytebuf 池。导致 大量 数据请求 直接 虚拟机内存 。下面贴一个 修改后的 参数配置
5、中间 设置过 容器的 jvm 参数,去控制内存大小。因为用的 Java 8 。需要加上最后两个参数去限制 元空间的 大小。(这只是 治标,最重要还是找到问题 )
jmap -heap 41356 打印heap的概要信息,GC使用的算法,heap(堆)的配置及JVM堆内存的使用情况
JAVA_OPTS="-server -Xms1g -Xmx1g -XX:PermSize=128m -XX:MaxPermSize=256m -XX:MaxNewSize=256m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m"
public void startServer(int port) {
try {
this.serverBootstrap = new ServerBootstrap();
if (SystemUtil.getOsInfo().isWindows()) {
this.bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("BOSSGROUP_"));
this.workerGroup = new NioEventLoopGroup(new DefaultThreadFactory("WORKGROUP"));
this.serverBootstrap.channel(NioServerSocketChannel.class);
} else {
this.bossGroup = new EpollEventLoopGroup(1, new DefaultThreadFactory("BOSSGROUP_"));
this.workerGroup = new EpollEventLoopGroup(new DefaultThreadFactory("WORKGROUP"));
this.serverBootstrap.channel(EpollServerSocketChannel.class);
}
this.serverBootstrap.group(bossGroup, workerGroup)
// 此hander是所有客户端连接都会经过的hander,就是只有这一个hander 单例
.handler(new LoggingHandler(LogLevel.WARN))
// 此hander是所有客户端都有一个hander,工厂模式创建出hander
.option(ChannelOption.SO_BACKLOG, 1024) // 输入连接指示(对连接的请求)的最大队列长度。如果队列满时收到连接指示,则拒绝该连接。FIFO(先进先出)的原则
.childOption(ChannelOption.SO_KEEPALIVE, true)//开启时系统会在连接空闲一定时间后像客户端发送请求确认连接是否有效
.childOption(ChannelOption.TCP_NODELAY, true)//关闭Nagle算法 NAGLE算法通过将缓冲区内的小封包自动相连,组成较大的封包,阻止大量小封包的发送阻塞网络,从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法;
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(8 * 1024, 32 * 1024))
// .childOption(ChannelOption.SO_LINGER, 5)//连接关闭时,偿试把未发送完成的数据继续发送,(等待5秒)
.childOption(ChannelOption.SO_SNDBUF, 1048576)//系统sockets发送数据buff的大小
.childOption(ChannelOption.SO_RCVBUF, 1048576)//---接收
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)//使用bytebuf池, 默认不使用
.childOption(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT);//使用bytebuf池, 默认不使用
System.out.println("内网端口启动:" + port);
LOGGER.info("Netty start at port:" + port);
this.serverBootstrap.bind(new InetSocketAddress(port)).sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}