Search code examples
javagarbage-collectionjnagarbage

Java JNA 4.3.0 Creates Insane Amout of Garbage When Looking for Process


JNA is making an insane amount of garbage when I'm trying to find a fake process by name.

Here is a screenshot of the allocations (about 100k.sec)

enter image description here

Here is the test case (used 4.3.0 SNAPSHOT of JNA)

import com.sun.jna.Native;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Tlhelp32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;

/**
 * Created by Jonathan on 8/26/2016.
 */
public class Main {

    public static void main(String[] args) {
        while (true)
            openProcess("doesntexist.exe");
    }

    private static final WinDef.DWORD DWORD_ZERO = new WinDef.DWORD(0);
    private static final Tlhelp32.PROCESSENTRY32 entry = new Tlhelp32.PROCESSENTRY32.ByReference();

    private static WinNT.HANDLE openProcess(String processName) {
        WinNT.HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPALL, DWORD_ZERO);
        try {
            while (Kernel32.INSTANCE.Process32Next(snapshot, entry)) {
                String fileName = Native.toString(entry.szExeFile);
                if (processName.equals(fileName))
                    return Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_ALL_ACCESS, true, entry.th32ProcessID.intValue());
            }
        } finally {
            Kernel32.INSTANCE.CloseHandle(snapshot);
        }
        return null;
    }

}

And finally here is the memory snapshot https://dl.dropboxusercontent.com/u/91292881/ShareX/2016/08/JNA%204.3.0.snapshot


Solution

  • I strongly suspect your heap usage is the repeated creation of new String objects inside your loop.

    Your code executes the process search (which looks through every process) inside another endless loop so you're effectively executing this line over and over...

    String fileName = Native.toString(entry.szExeFile);
    

    Internally, Native.toString(char[] buf) is creating a new String() each time:

    public static String toString(char[] buf) {
        int len = buf.length;
        for (int index = 0; index < len; index++) {
            if (buf[index] == '\0') {
                len = index;
                break;
            }
        }
    
        if (len == 0) {
            return "";
        } else {
            return new String(buf, 0, len);
        }
    }
    

    These String objects are used once to test equality and then are tossed on to the heap.

    Since the main purpose of creating the String is to test equality, you can probably test equality directly using the underlying char[] array. Convert processName to a char[] and iterate simultaneously over both arrays, testing for character equality up to the length of processName with a null terminator in the next character of entry.szExeFile.