jacob-project
jacob-project copied to clipboard
Memory leak
I have the following simple class calling my COM object. The class is part of a web service, and may be called from different Java threads, but the calls are serialized by the synchronization.
What it does is create a new ActiveX component, call the OpenDb
method on the component, then call the ExecuteHttpJson
method. If another request comes in, ExecuteHttpJson
is called again. After 10 seconds of inactivity, the ActiveX component is disposed. The reason for this is that the OpenDb
call is quite costly.
The problem is that there is a memory leak somewhere. The java process ends up consuming gigabytes of memory. Do you spot somewhere I'm creating the memory leak, or am I using your library completely wrong?
package org.codehive.telemator.core.repository;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComException;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicLong;
public class ComTelemator {
private static final long TIMER_DELAY = 10000;
private static final AtomicLong counter = new AtomicLong(0);
private static final Object lock = new Object();
private static Dispatcher dispatcher;
private final String databaseLocation;
private final Timer timer;
private TimerTask task;
public ComTelemator(final String databaseLocation) {
this.databaseLocation = databaseLocation;
final long id = counter.getAndIncrement();
this.timer = new Timer("DispatcherCleaner " + id, true);
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
}
public String executeHttpJson(final String method, final String uri, final String json) throws TelematorException {
synchronized (lock) {
try {
return getDispatcher().dispatch("ExecuteHttpJson", method, uri, json);
} catch (final TelematorException | RuntimeException ex) {
close();
throw ex;
}
}
}
void close() {
synchronized (lock) {
if (dispatcher != null) {
dispatcher.close();
dispatcher = null;
}
if (this.task != null) {
this.task.cancel();
this.task = null;
}
}
}
private Dispatcher getDispatcher() throws TelematorException {
if (dispatcher == null) {
dispatcher = new Dispatcher();
dispatcher.dispatch("OpenDb", this.databaseLocation, null);
}
resetTimer();
return dispatcher;
}
private void resetTimer() {
if (this.task != null) {
this.task.cancel();
}
this.task = new TimerTask() {
@Override
public void run() {
close();
}
};
this.timer.schedule(this.task, TIMER_DELAY);
}
private static final class Dispatcher {
private static final String PROG_ID = "Telemator.Document";
private final ActiveXComponent telemator;
public Dispatcher() {
this.telemator = new ActiveXComponent(PROG_ID);
}
public String dispatch(final String name, final Object... attributes) throws TelematorException {
try {
final Variant result = Dispatch.call(this.telemator, name, attributes);
return result.getString();
} catch (final ComException ex) {
throw new TelematorException(ex.getHResult(), message);
}
}
public void close() {
this.telemator.getObject().safeRelease();
}
}
}
There is a memory leak in Java_com_jacob_com_Dispatch_invokev(), in the case where the name2ID() call fails: ReleaseStringUTFChars() is not called,
The OP doesn't say that there was an exception thrown which is the case where that leak occurs .