vendredi 17 juillet 2020

Reusing JNI after fork()

First of all: I know that you cannot fork() a multi-threaded process without doing harm, as well as I did already read some articles adressing JVM specifically such as What happens to Java heap when fork is called from JNI thread.

Still I am looking for a solution for a problem I am currently facing: I have a TCP/IP server running on Linux, using boost and a classic fork() based approach for each connection. The clients send some data in a well-defined format and the server performs several computations. During and at the end of execution it uses JNI to access a postgres database from within a JAVA application (.jar).

My code currently works as expected as I do initialize the JVM in each child process again and again:

        // child process
        if (fork() == 0)
        {
            m_io_service.notify_fork(boost::asio::io_service::fork_child);

            // child process won't accept any new connection, socket is still open in parent process
            // fork does duplicate all fds!
            m_acceptor.close();

            // do not process SIGCHLD signal in child process
            m_signal.cancel();

            // boost::posix_time::ptime now = boost::posix_time::microsec_clock::local_time();
            NativeInterface::create(NativeInterface::ContextType::DEFAULT_CONTEXT, NativeInterface::ExecutionMode::ONLINE);
            // boost::posix_time::ptime than = boost::posix_time::microsec_clock::local_time();
            // boost::posix_time::time_duration diff = than - now;
            // std::cout << diff.total_milliseconds() << std::endl;

            start_read();
        }
        else
        {
            m_io_service.notify_fork(boost::asio::io_service::fork_parent);

            m_socket.close();
            start_accept();
        }

The time-measuring (which is outcommened in the code above) points out, that the time spent in JNI related code takes about ~1,6sec. This is mainly a call to JNI_CreateJavaVM, FindClass and some GetMethodID calls. While loading the mentioned class the java library connects to the database in a static block (each time the module gets loaded, once). I am almost certain this causes most of the time tracked above.

The thing is: the entire execution of the program does take less than 1sec and it is not feasibly to get an overhead of 1,5sec for each request just because of connecting to a database und loading the java library.

I don't really feel like rebuilding the tcp/ip server to use threads instead of fork(), in the end this might get even worse in overall performance. Creating the JVM in the parent process and relying on fork() to copy everything does not work as well as stated above.

Any ideas or other solutions? I am not sure about the JAVA part if it might be possible to have the library running as some kind of service and at least spare establishing the connection each time, however, changing the JAVA code is also not planned for now, even if possible. It should be a solution that takes place in C++ and JNI.

Aucun commentaire:

Enregistrer un commentaire