前段时间java程序,内存泄漏比较严重,平均3-5天就要重启一下,赶快分析原因。从公司的监控看到,主要是对外内存泄漏,因为堆内存泄漏不是很严重。所以决定优先处理前者。因为该项目是java开发的,主要任务时加载tensorflow1.*的模型,并实时预测。其实主要JNI调用c++接口,所以很大可能是在接口调用时泄漏了,看代码:
Tensor res =null; try { List<String> colname=IntColname; Runner rlt = sess.runner(); for(int i=0; i<intvalue[0].length; i++) { int[][] index=new int[intvalue.length][1]; for(int m=0; m<intvalue.length; m++) { index[m][0]=intvalue[m][i]; } Tensor indexTensor = Tensor.create(index); rlt.feed(colname.get(i), indexTensor); } colname=FloatColname; for(int i=0; i<floatvalue[0].length; i++) { float[][] index=new float[floatvalue.length][1]; for(int m=0; m<floatvalue.length; m++) { index[m][0]=floatvalue[m][i]; } Tensor indexTensor = Tensor.create(index); rlt.feed(colname.get(i), indexTensor); temp.add(indexTensor); } res=rlt.fetch("output").run().get(0); float[][] finalRlt = new float[intvalue.length][1]; res.copyTo(finalRlt); List<Float> result=new ArrayList<Float>(); for(int i=0; i<finalRlt.length; i++) { result.add(finalRlt[i][0]); } return result; } catch (Exception e) { logger.error("",e); }finally { if (res != null) { res.close(); } }
虽然res调用了close方法,但是 indexTensor 却没有调用,因此调整代码。
Tensor res =null; List<Tensor> temp=new ArrayList<>(); try { List<String> colname=IntColname; Runner rlt = sess.runner(); for(int i=0; i<intvalue[0].length; i++) { int[][] index=new int[intvalue.length][1]; for(int m=0; m<intvalue.length; m++) { index[m][0]=intvalue[m][i]; } Tensor indexTensor = Tensor.create(index); rlt.feed(colname.get(i), indexTensor); temp.add(indexTensor); } colname=FloatColname; for(int i=0; i<floatvalue[0].length; i++) { float[][] index=new float[floatvalue.length][1]; for(int m=0; m<floatvalue.length; m++) { index[m][0]=floatvalue[m][i]; } Tensor indexTensor = Tensor.create(index); rlt.feed(colname.get(i), indexTensor); temp.add(indexTensor); } res=rlt.fetch("output").run().get(0); float[][] finalRlt = new float[intvalue.length][1]; res.copyTo(finalRlt); List<Float> result=new ArrayList<Float>(); for(int i=0; i<finalRlt.length; i++) { result.add(finalRlt[i][0]); } return result; } catch (Exception e) { logger.error("",e); }finally { if (res != null) { res.close(); } for(int i=0; i<temp.size(); i++) { Tensor t=temp.get(i); if(t!=null) { t.close(); } } }
主要是增加temp队列回收临时变量。上线后发现泄漏不是很严重了,说明很有效果。不过还有,后面是java堆内存泄漏,发现每次泄漏的时间正是模型切换的时间,因此大概率是模型切换的代码有问题,上代码。
public static void clearCache() { logger.info("===model clear==="); sess=null; }
这里的session就是模型解析好的session会话,由下面的代码生成。
byte[] graphBytes = IOUtils.toByteArray(inputStream); Graph graph=new Graph(); graph.importGraphDef(graphBytes); sess = new Session(graph);
立刻明白,上面的代码有问题,调整为如下:
public static void clearCache() { logger.info("===model clear==="); if(sess!=null) { logger.info("destory session"); sess.close(); sess=null; } }
少了一个close,造成session关联的对象无法释放,至此内存泄漏问题算是解决了。