排查JVM异常与JProfiler分析Dump记录
添加参数
-Djava.rmi.server.hostname=192.168.129.129 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -
Dcom.sun.management.jmxremote.ssl=false参数解析
-Djava.rmi.server.hostname=192.168.129.129 #被监控服务器ip
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=1099 #开放连接的端口
-Dcom.sun.management.jmxremote.authenticate=false #关闭认证
-Dcom.sun.management.jmxremote.ssl=false #关闭SSL校验
#保存GC日志
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -Xloggc:/jvm-gc-logs/gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=20M
#保存OOM日志
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/jvm-oom-logs/ -XX:ErrorFile=/jvm-oom-logs/hs_err.log实战:
最近公司生产环境服务出现运行时间久系统卡顿的问题,每次重启完流畅度就恢复,CPU内存占用未见异常,观察Grafana发现gc比较频繁,jvm内存增加较快,尝试导出Dump快照通过JProfiler查看分析发现惊天大坑
JProfiler启动

发现端倪,char[]高达1230MB的占用😁👍

切换最大对象查看来自哪个类

前往项目查找,发现以下为某离职同事前作,貌似没有什么问题
public class FilterPath {
private static StringBuffer PATH_LISTS = new StringBuffer();
public static String getPathLists(){
String name = "hamp_point_end";
PATH_LISTS.append("/"+name+"/login");
PATH_LISTS.append("/"+name+"/favicon.ico");
PATH_LISTS.append("/"+name+"/swagger");
PATH_LISTS.append("/"+name+"/configuration");
PATH_LISTS.append("/"+name+"/images");
PATH_LISTS.append("/"+name+"/v2/api-docs");
PATH_LISTS.append("/"+name+"/BaseUser/selectByUserName");
PATH_LISTS.append("/"+name+"/BaseUserSecurity/getSecurityNotSession");
PATH_LISTS.append("/"+name+"/BaseSendSmsController/sendCodeByAccount");
PATH_LISTS.append("/"+name+"/BaseSendSmsController/compareCode");
PATH_LISTS.append("/"+name+"/BaseUser/test");
PATH_LISTS.append("/"+name+"/BaseDingAPIController/test");
PATH_LISTS.append("/"+name+"/as/alarmRealHisTime/test");
PATH_LISTS.append("/"+name+"/auth");
PATH_LISTS.append("/"+name+"/ServerController/server");
PATH_LISTS.append("/"+name+"/BaseUserSecurity/judgeSecurity");
PATH_LISTS.append("/"+name+"/BaseConfigController/selectByDoor");
PATH_LISTS.append("/"+name+"/BaseUser/selectByAccountToBaseUser");
PATH_LISTS.append("/"+name+"/BaseUser/updateByAccount");
PATH_LISTS.append("/"+name+"/BaseRoleController/selectByRid");
PATH_LISTS.append("/"+name+"/captcha");
PATH_LISTS.append("/"+name+"/verifyCaptcha");
return PATH_LISTS.toString();
}
}查看此类方法引用💩,小刀拉屁股开了眼,每次接口请求都调用一次此方法重新增加路径并返回静态StringBuffer.....
@Component
public class AuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private BaseUserMapper userMapper;
@Autowired
private BaseAccessFeatureMapper baseAccessFeatureMapper;
@Autowired
private RedisService redisService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
if("10.25.20.188".equals(request.getRemoteAddr())){
chain.doFilter(request, response);
}else{
String tenantId = request.getHeader(Constants.TENANT_ID);
if(tenantId!=null && !"".equals(tenantId)){
TenantContextHolder.setTenantId(tenantId);
}else{
TenantContextHolder.setTenantId("0");
}
String token = request.getHeader("token");//获取header中的验证信息
String roleId = ((String)redisService.get("roleId"+token));
String uid = ((String)redisService.get(token));
System.out.println("******查询*********"+request.getRemoteAddr()+tenantId);
System.out.println("******查询*********"+"token:"+token+"uid:"+uid+"roleId:"+roleId);
if("superAdmin".equals(token)||request.getRequestURI().indexOf("login")!=-1 || request.getRequestURI().indexOf("favicon.ico")!=-1 || request.getRequestURI().indexOf("swagger")!=-1 ||
request.getRequestURI().indexOf("configuration")!=-1 || request.getRequestURI().indexOf("images")!=-1 || request.getRequestURI().indexOf("/v2/api-docs")!=-1
|| FilterPath.getPathLists().indexOf(request.getRequestURI()) != -1 || request.getRequestURI().indexOf("druid")!=-1 ||request.getRequestURI().indexOf("webSocket")!=-1){
BaseUser baseUser = new BaseUser();
if (roleId != null && !"".equals(roleId)) {
baseUser.setRoles((String) redisService.get(roleId.substring(7)));
}
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(baseUser, null, baseUser.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
//设置为已登录
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
return;
} else {
....屎上雕花,修改成初始化首次加载返回固定静态StringBuffer😁👍
@Component
public class FilterPath {
private static final Logger logger = LoggerFactory.getLogger(FilterPath.class);
private static StringBuffer PATH_LISTS = new StringBuffer();
public static String getPathLists() {
return PATH_LISTS.toString();
}
@PostConstruct
public void setPathLists() {
logger.info("加载匿名接口到缓存中................");
String name = "hamp_point_end";
PATH_LISTS.append("/" + name + "/login");
PATH_LISTS.append("/" + name + "/favicon.ico");
PATH_LISTS.append("/" + name + "/swagger");
PATH_LISTS.append("/" + name + "/configuration");
PATH_LISTS.append("/" + name + "/images");
PATH_LISTS.append("/" + name + "/v2/api-docs");
}
}