生产服务内存泄露排查

文章目录

  • 前言
  • 一、排查过程


前言

生产环境,我们使用rancher k8s部署我们的服务,有个服务(具体我这里就不说了),在晚上8点左右,因为这个服务的内存溢出导致了其他服务出现了异常,在8点的时候,客户频繁投诉,后面排查发现是有个服务内存泄露。通过grafana图形观察,可以明显看到其实这个服务在早市11点左右其实已经内存泄露,只是当时客户并没有发现。

一、排查过程

因为生产已经挂了,当时我们进入服务的pod中简单执行如下命令。不要问为什么这么做,其实这个内存泄露问题已经出现过几次了,但是不知道问题点在哪,刚好这次复现,就赶紧找到罪魁祸首

jps -lm
jmap -histo 6| head -20

先找到占用内存最大的实体类

生产服务内存泄露排查_第1张图片
还好,当时这个服务用到的FineBiEquipmentFeign比较集中,这个是提供数据给报表服务用的。
找到代码如下

/***
     * type:1.设备历史参数查询报表 2.设备历史参数查询趋势图
     * @return
     */
    @RequestMapping(value = "/selectItem", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity selectItem(@RequestBody FineBiEquipmentQuery fineBiQuery) {
        try {
            log.info("getEndTime========={}" + fineBiQuery.getEndTime());
            log.info("getBeginTime========={}" + fineBiQuery.getBeginTime());
            long period = (Long.parseLong(fineBiQuery.getEndTime()) - Long.parseLong(fineBiQuery.getBeginTime())) / 1000;
            log.info("period========={}" + period);
            Map<String, Object> maps = new HashMap<>(16);
            SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            log.info("fineBiQuery========={}" + JSONUtil.toJsonStr(fineBiQuery));
            List<FineBiEquipmentFeign> list = fineBiService.selectAllType(fineBiQuery);
            log.info("list==========={}" + list.toString());
            List<FineBiEquipmentFeign> mapList = new ArrayList<>(16);
            List<FineBiEquipmentFeign> synchronizedList = Collections.synchronizedList(mapList);
            long startTime = System.nanoTime();

            list.parallelStream().forEach(infoList -> {
                JSONObject map = new JSONObject(16);
                JSONArray jsonArray = new JSONArray(16);
                String[] deviceParmaIds = {DeviceParamUtil.createDeviceParmaId(infoList.getDevice_id()
                        , infoList.getPart_id(), infoList.getParam_id(), infoList.getSource_id())};
                map.put("paramIds", deviceParmaIds);
                map.put("deviceId", infoList.getDevice_id());
                map.put("period", period);
                map.put("endTs", Long.parseLong(fineBiQuery.getEndTime()));
                jsonArray.add(map);
                //第三方API接口(本场景为外调接口,根据公司框架和业务自行修改)
                // RestTemplate restTemplate = new RestTemplate();
                //ResponseEntity dataByKeys = restTemplate.postForEntity("http://kong.ele-iot-sit.10.74.20.12.nip.io/api/v2/persist/tsdb/data/keys", jsonArray, JSONArray.class);
                ResponseEntity<JSONArray> dataByKeys = persistClient.getDataByKeys(jsonArray);
                log.info("dataByKeys========={}" + JSONUtil.toJsonStr(dataByKeys));
                if (dataByKeys.getStatusCodeValue() == HttpStatus.HTTP_OK) {
                    Objects.requireNonNull(dataByKeys.getBody()).forEach(info -> {
                        //返回结果 -最外层转成map
                        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(info));
                        Object resMap = jsonObject.get("resMap");
                        String resMap1 = JSON.toJSONString(resMap);
                        if (!resMap1.contains("{}")) {
                            JSONObject mapObject = JSONObject.parseObject(resMap1);
                            mapObject.entrySet().parallelStream().forEach(keyList -> {
                                FineBiEquipmentFeign finebiequipmentfeign = new FineBiEquipmentFeign();
                                BeanUtils.copyProperties(infoList, finebiequipmentfeign);
                                String format = sf.format(new Date(Long.parseLong(String.valueOf(keyList.getKey()))));
                                finebiequipmentfeign.setItem(format);
                                if (!keyList.getValue().toString().contains("/") && StringUtils.isNotNullOrEmpty(keyList.getValue().toString())) {
                                    finebiequipmentfeign.setInfo(keyList.getValue().toString());
                                }
                                synchronizedList.add(finebiequipmentfeign);
                            });
                        } else {
                            synchronizedList.add(infoList);
                        }
                    });
                } else {
                    synchronizedList.add(infoList);
                }
            });
            List<FineBiEquipmentFeign> result=new ArrayList<>();
            if (fineBiQuery.getType()==1){
                result=synchronizedList.stream().sorted(Comparator.comparing(FineBiEquipmentFeign::getItem,
                        Comparator.nullsLast(Comparator.naturalOrder())).reversed()).collect(Collectors.toList());
            }else {
                result=synchronizedList.stream().sorted(Comparator.comparing(FineBiEquipmentFeign::getItem,
                        Comparator.nullsLast(Comparator.naturalOrder()))).collect(Collectors.toList());
            }
            long endTime = System.nanoTime();
            log.info("查询耗时:" + (endTime - startTime) / 1000 / 1000);
            log.info("总共:" + result.size() + "条数据===========================>");
            log.info("result:{}", JSONUtil.toJsonStr(result));
            maps.put("records", result);
            return successResult(maps);
        } catch (Exception e) {
            log.error("查询报表失败:{}", e);
            return errorResult();
        }
    }

最开始肉眼看代码,没有看出什么问题
后面实在没有办法,最开始在sit环境测试想复现这个问题,后面发现还是不行。因为sit没有数据上报的数据,数据量不够,所以在直接在uat环境上测试。经过测试只要报表查询数据量足够大,就会导致堆栈溢出
在内存溢出后,在查看堆栈中对象大小排名,明细看到对象已经被释放掉,所以说明代码是没有问题的。真正的问题,是数据量大,导致一直往synchronizedList集合存放数据,导致的内存溢出

你可能感兴趣的:(java)