62,634
社区成员




import com.mathworks.toolbox.javabuilder.MWException;
import myCal.MyCal;
public class CalTest {
static MyCal myCal;
static {
try {
myCal = new MyCal();//matlab计算对象
} catch (MWException e) {
e.printStackTrace();
}
}
static void cal() throws MWException {
long t0 = System.currentTimeMillis();
for (int i = 1; i <= 30000; i++) {
//调用matlab函数计算结果(jni调用)
Object[] calResult = myCal.myCal(4, 2, 3);
if ((i % 10000) == 0) {
long t1 = System.currentTimeMillis();
//每1w次打印执行时间, 发现调用越多则调用越慢
System.out.println("第" + (i / 10000) + "w用时:" + (t1 - t0) / 1000);
t0 = t1;
}
}
}
public static void main(String[] args) throws Exception {
cal();
Thread.sleep(30000);
System.out.println("=====");
cal();
}
}
for (Object o : calResult) {
MWNumericArray m = (MWNumericArray) o;
m.dispose();
}
通过debug发现 calResult 实际类型是MWNumericArray, 这个是matlab转为java后的一种数据类型(矩阵、向量、数值), 里面的一个NativeArray 很明显就是matlba 的数据(byte[])。 NativeArray 的 finalize方法被重写调用dispose()。 上面m.dispose(); 也就是调用内部NativeArray 的dispose()。
看到这里大家获取有些疑惑, 因为finalize方法被垃圾回去的时候会自动被调用,也就是说calResult 出了作用域以后即会被调用dispose方法, 我们无需加入上面的代码。 我也真实因为这个观点才犯错的。
这里有篇文章对于finalize可能引发内存溢出 http://ju.outofmemory.cn/entry/74671
在例子(生产的例子不是测试例子)中并不会内存溢出(jvm设置的内存比较大), 但问题确实是Finalizer线程比主线程优先级低,当这么大的一直循环创建了大量Finalizer对象, 却没能被调用执行。 这个我通过jmap弄出dump文件后证实确实如此, 在测试内存快慢的的时候几乎全被Finalizer对象占有。
手动调用dispose后正确解决了这个问题。想起之前看到Java编程思想还是jvm那本书里说 java程序员应该完全忘记finalize方法。
在国外网上查到所有调用matlab的例子中都会像上面显示调用dispose, 然后带句注释
//clean up the resources used by the generated MATLAB code. 获取
//free native resources
并没说dispose最终做了什么, 反编译出的代码里面是native调用。 我猜测是java调用Matlab使用了Direct Memory
然后我通过-XX:MaxDirectMemorySize=1M测试无变化,应该也不是Direct Memory被大量占用的问题。
目前就知道这些东西, 问题已经解决, 但我还是不知道Java调用matlab机制是什么, dispose的native方法到底做了什么。