Java program freezes for 10 seconds after receiving around 1000 UDP DatagramPackets then resumes again

I have a PC running a Unity C# application sending a UDP packet each few milliseconds to other Machines via network (2 KUKA robots on windows 7 embedded and running the same JAVA program, it has an i5 Intel processor, so it's pretty powerful). The Java program is supposed to receive these packets, parse their content (robot positions, coded in an array of 7 values separated by '#'), move and read again. The problem is, when the PC sends packets at a rate of 1 each 0.02 secs(this doesn't happen at 0.03 or above, it is a hardware limit?!), the java program freezes at around 1000 packets received (sometimes 955 or 986, etc.) for a 8-10 seconds, then resumes again. It does the same when it arrives to 2000, and 3000.
The program freezes at :

serverSocket.receive(receivedPacket); // receives the array of Bytes

I suspected the network switch, so connected the PC directly to on robot, but nothing changed. The weird thing is, it happens at the same time for the two robots, which made me suspect the PC. But, when my colleague started a console displaying in real-time the C# program sending packets, it didn't freeze when the java programs were frozen, and looked like these packets were lost.
I looked for similar questions on SO, many suspected buffers, so I'm thinking about creating a thread that listens to the UDP port and stores packets in a queue on the memory, then my main java program reads from that thread. Does it look like a viable track to follow? Any suggestions are welcome.

P.S. Here's the code :


package readers;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MyProgram {
    // Network variables 
    public static DatagramSocket serverSocket;
    private static DatagramPacket receivedPacket; 

    // Received data variables
    private static byte[] aReceivedData = new byte[1024];
    private static String sReceivedData;
    private static String sAxesInformationReceived;
    private static Double[] dAxesInformationReceived = new Double[7]; 

    // ******** MAIN ***************************************
    public static void main(String[] args) throws  Exception {  
        int mFramecount =0;
        int mPort = 30004; //default value
        int mTimeout = 20*1000; //default value
        int mFramelimit = (15 * 1000); //default value

        // Create UDP server socket
        try {
            serverSocket = new DatagramSocket(mPort);
            serverSocket.setReuseAddress(true);
            serverSocket.setSoTimeout(mTimeout);
        } catch (SocketException e) 
        {   System.err.println("socket bind fail"); closeSocket();e.printStackTrace(); return; }

        // Receive the UDP packet   
        try {
            receivedPacket = new DatagramPacket(aReceivedData, aReceivedData.length);
            serverSocket.receive(receivedPacket); // receive the array of Bytes
        } catch (Exception e) { closeSocket();  return; }  
        //Clear Buffer  
        for (int i = 0; i < 7; i++) {
            if(dAxesInformationReceived[i] == null)
            {
                dAxesInformationReceived[i] = 0.0;
            }
        }


        // <<<<<<<<<<<WHILE <<<<<<<<<<<<<<<<<<<< 
        while (true) {          

            //Clear Buffer
            for(int i=0; i < aReceivedData.length; i++)
            {
                aReceivedData[i]=0;
            }


            // Decoding and Parsing received values
            try {

                receivedPacket = new DatagramPacket(aReceivedData, aReceivedData.length); 
                serverSocket.receive(receivedPacket); // receive the array of Bytes

                byte[] byteData = new byte[receivedPacket.getLength()];
                System.arraycopy(receivedPacket.getData(), receivedPacket.getOffset(), byteData, 0,  receivedPacket.getLength()); 
                sReceivedData = new String(byteData, "UTF-8");   
                Pattern pattern = Pattern.compile("@(.*?)@"); // RegEx
                Matcher matcher = pattern.matcher(sReceivedData); 
                System.out.println("Data: '" + sReceivedData + "', || length: " + byteData.length + "|| Frame count="+ mFramecount ++);

                /*
                 * mFramecount++;
                        if (mFramecount %100 == 0) {
                            System.out.println("Data: '" + sReceivedData + "', || length: " + byteData.length + "|| Frame count="+ mFramecount );
                        }
                 */

                if (matcher.find()) {
                    sAxesInformationReceived = matcher.group(1);
                    String[] sAxesValuesInStringArray = sAxesInformationReceived.split("#");
                    if (sAxesValuesInStringArray.length != 7) {
                        System.err.println("[UnityControl] invalide number of axis");
                        break;
                    }
                    for (int i = 0; i < 7; i++) {
                        dAxesInformationReceived[i] = Double.parseDouble(sAxesValuesInStringArray[i]);
                    }
                } else {
                    System.err.println("[UnityControl] invalid format");
                    break;
                }
            } catch (Exception e) {
                System.err.println("[UnityControl] socket exception");
                e.printStackTrace();
                break;
            }



            /* THIS PART IS USING THE ROBOT's API */
            // Change destination according to the received position
            JointPosition framePos = new JointPosition(
                    Math.toRadians(dAxesInformationReceived[0]),
                    Math.toRadians(dAxesInformationReceived[1]),
                    Math.toRadians(dAxesInformationReceived[2]),
                    Math.toRadians(dAxesInformationReceived[3]),
                    Math.toRadians(dAxesInformationReceived[4]),
                    Math.toRadians(dAxesInformationReceived[5]),
                    Math.toRadians(dAxesInformationReceived[6]));

            try {
                if(runtime.setDestination(framePos)<0) 
                    break; // break when error planning robot motion
            }
            catch(Exception e)
            {
                System.err.println("Runtime exeption");
                break;
            }

            if(mFramecount >= mFramelimit) break;


        }
        // LOOP BACK  
    }

    //**********************************************************************
    static void closeSocket() {
        if (serverSocket != null) {
            serverSocket.disconnect();
            serverSocket.close();
            System.out.println("[UnityControl] socket closed");

        }
    }

}

screenshot

I did what @EJP suggested in his answer, and to better track the problem, I added the number of the packet in its end, and it appears that there's a loss of the UDP packets, on both machines (The PC's log says it didn't stop sending meanwhile). Here's a log from both machines running the same code:

Log two machines

2 answers

  • answered 2017-01-11 14:24 Alex Baranowski

    There is high probability that problem is with you GC (Garbage Collection) that is making thing called stop the world. Stop the world freeze application and clean memory from not used objects :). You can obtain your program PID, then connect jconsole to see what is happening with your memory.

    There is also possibility that setting more memory would help. java -Xms1024m -Xmx1024m -Xms set initial Java heap size -Xmx set maximum Java heap size

    If you are using many threads there is possibility that creating thread is so memory and time consuming - then you can use Thread pool.

    Unfortunetelly I'm not able to help you more without any code.

  • answered 2017-01-11 14:24 EJP

    You're creating much more garbage than necessary. You don't need byteArray at all, or the System.arraycopy():

    sReceivedData = new String(receivedPacket.getData(), 0, receivedPacket.getLength(), "UTF-8");
    

    You also don't need to keep recompiling the same regular expression into a Pattern.

    You also don't need to zero the byte array prior to the receive.

    NB Calling setReuseAddress() after constructing the DatagramSocket (and therefore binding it) is a complete waste of time.