java-native-access / jna

Java Native Access
Other
8.51k stars 1.68k forks source link

Optimize GC behaviour #694

Open jonatino opened 8 years ago

jonatino commented 8 years ago

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)

http://i.stack.imgur.com/u3D2I.png

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

matthiasblaesing commented 8 years ago

Could you please describe why you see a problem here? I'm running your sample with visual VM and looking at the CPU graph, the GC times are not existing (only visible because the 'running' graph breaks in at that point) and heap memory usage is not excessive (< 25MB) and stays there.

I had a look at this with VisualGC and it looks as if the objects are allocated from the Eden Space and never transition to the longer living spaces. As this is tight loop I see this as a worst case scenario. Where do you see the issue?

matthiasblaesing commented 8 years ago

Another info: It seems the memory snapshot you mention is only usable with yourkit. I don't have a license for that, so please give some numbers, that support your claim of a problem.

jonatino commented 8 years ago

@matthiasblaesing The issue is in low latency programs (such as mine) require as little gc pauses as possible. You shouldn't create 100kobjs/sec and hope the GC cleans up when you can easily avoid it. My VisualVM is broken unfortunately so I can't get you a snapshot there.

Yourkit will provide you with a lifetime license if you have an open source project (such as JNA), you just need to contact their support :)

matthiasblaesing commented 8 years ago

@Jonatino by all means: I'm not against optimizations and I'm willing to review changes you'll suggest, but be careful with "when you can easily avoid". Please justify this easily by providing a PR with the optimizations you suggest.

cosmicdan commented 6 years ago

@matthiasblaesing The issue is in low latency programs (such as mine) require as little gc pauses as possible. You shouldn't create 100kobjs/sec and hope the GC cleans up when you can easily avoid it.

So tune your GC for low latency? The VM can only do so well on the defaults, trying to balance everything.

You probably should also move that String creation outside the loop and do a direct mapping of Process32Next.

saudet commented 6 years ago

@Jonatino FYI, you might be interested in using the Windows API with JavaCPP: https://github.com/bytedeco/javacpp-presets/tree/master/systems It tends to produce much less garbage than JNA especially when deallocating objects explicitly: http://bytedeco.org/news/2015/10/25/moving-to-java-se-7/ http://bytedeco.org/news/2018/07/17/bytedeco-as-distribution/

dbwiddis commented 5 years ago

I know this is an old issue but I just stumbled on it and it looked oddly familiar. Seems I answered this question on StackOverflow a few days after it was posted here, and the OP accepted my answer.

The source of the garbage in the test case is Native.toString() which creates a new String object, which is used on the next line and then tossed onto the heap. Directly comparing the character arrays would avoid creating the new Strings.

I think this issue can be closed.