maxHttpHeaderSize带来的问题
前因
我们的服务是部署在docker容器中,使用SpringBoot框架搭建的微服务,jdk版本是open jdk 1.8_u201版,内存分配了4G,共部署了4个微服务,使用gateway作为网关负载均衡。有一天运营团队通知我,我们的服务不能访问,访问的页面都是没有数据的;我随即查看我们的系统运行状况,我是通过docker exec -it <容器ID> jstat -gcutil <pid> 1000
来查看的;发现系统的年轻代占用50%,老年代占用97%,元空间占用93%,查看GC次数发现没有触发FullGC,只是触发了YGC 68次。
这下让我产生疑问,为什么内存几乎被占满了,jvm还不进行FullGC呢?
于是我通过命令docker exec -it <容器ID> jmap -dump:file=<filename> <pid>
来生成dump快照文件,还有获取项目中的gc日志。而且通过MAT分析dump、使用gcviewer分析GC日志。
MAT分析工具 https://www.eclipse.org/mat/downloads.php
gcviewer https://github.com/chewiebug/GCViewer/releases
先来看看GC日志情况
java -jar ./gcviewer.jar service_gc.log
这里可以看到总的GC暂停次数和时间,和FullGC暂停次数和时间
项目刚刚启动时,GC情况,jvm堆内存逐步变大,黄色代表年轻代,紫色代表老年代。
到最后的阶段
我们再来看看MAT分析情况是怎样:
出现两个可能发生内存溢出的问题
- byte[]占用堆内存比例约为46.24%
- 有61个实例Http11OutputBuffer被系统加载,总共耗费内存46.16%
这里的指向的问题的线程是org.apache.tomcat.util.threads.TaskThread
继续往下看,发现跟我们项目有点接近的东西tk.mybatis.mapper.mapperhelper.EntityHelper
,这个是实体类工具类 - 处理实体和数据库表以及字段关键的一个类。我们使用了这个插件,相信大家用过mybatis都会知道MyBatisPlus,其实tk.mapper做的功能也是和MyBatisPlus差不多。
那么这里为啥会装那么多的tk.mapper对象呢,主要来源是查数据库后转换实体类而创建的,我们再看看他的GC Roots最近节点
发现都是在org.apache.tomcat.util.threads.TaskThread
类引用,我们打开这个类看看源码。
终于找到了与tomcat相关的类了,开心!!!!!
public class Nio2Endpoint extends AbstractJsseEndpoint<Nio2Channel,AsynchronousSocketChannel> {
...
@Override
public void bind() throws Exception {
// Create worker collection
if (getExecutor() == null) {
createExecutor(); // 统一在这个方法创建线程池
}
if (getExecutor() instanceof ExecutorService) {
threadGroup = AsynchronousChannelGroup.withThreadPool((ExecutorService) getExecutor());
}
// AsynchronousChannelGroup needs exclusive access to its executor service
if (!internalExecutor) {
log.warn(sm.getString("endpoint.nio2.exclusiveExecutor"));
}
serverSock = AsynchronousServerSocketChannel.open(threadGroup);
socketProperties.setProperties(serverSock);
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
serverSock.bind(addr, getAcceptCount());
/