JNA X11 和 XFree

JNA X11 and XFree

进行低级 X11 编程时,通常需要对返回的结构调用 XFree()。例如 XGetWMName,我在下面的代码中完成了这个,然后在几秒钟后用

爆炸了
AssertionMessage: *** Error in `/opt/jdk1.8.0_40/bin/java': double free or corruption (fasttop): 0x00007f7ca822bdd0 ***

这是由 XFree() 调用引起的,如果删除它,问题就会消失。但是用 top 监控进程显示稳定的内存增长表明我确实需要它...

public class X11WindowFinder {

    private X11 x11;

    public X11WindowFinder() {
        x11 = X11.INSTANCE;
    }

    public List<Window> find(Pattern title) {
        Display display = x11.XOpenDisplay(null);
        Window root = x11.XDefaultRootWindow(display);
        List<Window> windows = recurse(x11, display, root, title);
        x11.XCloseDisplay(display);
        return windows;
    }

    private synchronized List<Window> recurse(X11 x11, Display display, Window root, Pattern pattern) {
        List<Window> windows = new ArrayList<>(1);
        X11.WindowByReference windowRef = new X11.WindowByReference();
        X11.WindowByReference parentRef = new X11.WindowByReference();
        PointerByReference childrenRef = new PointerByReference();
        IntByReference childCountRef = new IntByReference();

        x11.XQueryTree(display, root, windowRef, parentRef, childrenRef, childCountRef);
        if (childrenRef.getValue() == null) {
            return Collections.emptyList();
        }

        long[] ids = {};

        if (Native.LONG_SIZE == Long.BYTES) {
            ids = childrenRef.getValue().getLongArray(0, childCountRef.getValue());
        } else if (Native.LONG_SIZE == Integer.BYTES) {
            int[] intIds = childrenRef.getValue().getIntArray(0, childCountRef.getValue());
            ids = new long[intIds.length];
            for (int i = 0; i < intIds.length; i++) {
                ids[i] = intIds[i];
            }
        }

        for (long id : ids) {
            Window child = new Window(id);
            X11.XTextProperty name = new X11.XTextProperty();
            x11.XGetWMName(display, child, name);
            String value = name.value;
            if (value != null) {
                System.out.println(String.format("Found window %s free %s", value, name));
            }
            if (value != null && pattern.matcher(value).matches()) {
                windows.add(child);
            }
            x11.XFree(name.getPointer());
            windows.addAll(recurse(x11, display, child, pattern));
        }
        return windows;
    }

    public static void main(String[] args) {
        X11WindowFinder finder = new X11WindowFinder();
        while (true) {
            finder.find(Pattern.compile(".*Firefox.*"));
        }
    }

}

JNA 中的指针似乎使用 finalize() 来释放内存,即 com.sun.jna.Memory.finalize()

/** Properly dispose of native memory when this object is GC'd. */
protected void finalize() {
    dispose();
}

因此,如果我调用 XFree(),我冒着被双重释放的风险...这个假设是否正确。我能找到几个 JNA 的例子,尤其是 X11 平台相关的。

您想释放 XTextProperty 结构的 value 字段中的指针,而不是结构本身。

来自XTextProperty man page:

To free the storage for the value field, use XFree.

根据您的代码(请注意 value 字段应该是 Pointer 而不是 String):

x11.XTextProperty name = new X11.XTextProperty();
x11.XGetWMName(display, child, name);
// ... do some stuff
x11.XFree(name.getPointer());

编辑 请注意,根据 XTextProperty 的当前定义,value 字段映射到 Java String,这有效地隐藏了原始指针。您需要使用 XTextProperty.getPointer(XXX)(其中 XXX 是 value 字段的偏移量)才能访问要传递给 XFree 的原始指针值。由于 value 字段在结构中排在第一位,因此使用 Structure 本身的基地址也可以工作(即 Structure.getPointer())。

XFree 的调用实际上应该是 XTextProperty 自己的内存清理(终结器)的一部分。