I did it on purpose ‘OutOfMemoryError’

Please check the github repo : https://github.com/khalidOubelque/jmeterTesting

So i have a spring boot where tried to overload it, it was a fun experience, since my computer was close to shut down.

So as you can see below i created a jmeter scenario that consist on running 10k VU in 1s and loop 2 times

This test will sends GET & POST HTTP requests to my spring boot app, implenting TLS SSL hanshake.

For JVM args this is was the configuration i used :

And this is my controller :

@RestController
@RequestMapping("/api/v1/test")
public class TestControler {
    private int counter = 0;

    @GetMapping(value = "/getMessage")
    public ResponseEntity testMessgage() {
        System.out.println("HTTP GET request counter "+counter++);
        return ResponseEntity.status(HttpStatus.ACCEPTED)
                .body("Hello world");
    }


    @PostMapping(value = "/employe", consumes = MediaType.APPLICATION_JSON_VALUE)
    public void testHttpPostRequest(@RequestBody Employe employe){
        System.out.println("HTTP POST request employe name "+employe.getName()+" employe badge "+employe.getBadgeNum());
    }

    @ResponseBody
    @ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
    public String handleHttpMediaTypeNotAcceptableException() {
        return "acceptable MIME type:" + MediaType.APPLICATION_JSON_VALUE;
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public void handleError(Exception ex) {
        // TODO: log exception
    }
}

After few seconds i got OutOfMemoryError

java.lang.OutOfMemoryError: Java heap space
Exception in thread "https-jsse-nio-8443-exec-212" java.lang.OutOfMemoryError: Java heap space
Exception in thread "https-jsse-nio-8443-exec-46" java.lang.OutOfMemoryError: Java heap space

Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "https-jsse-nio-8443-exec-211"

At the same time tried to generate HeapDump : A heap dump (hprof dump)is a snapshot of all the objects that are in memory in the JVM at a certain moment. They are very useful to troubleshoot memory-leak problems and optimize memory usage in Java applications. 

/c/'Program Files'/Java/jdk-17.0.3.1/bin/jmap -dump:live,format=b,file=/dev/shm/dump-20220212_1541.hprof 1298

Using MAT Eclipse Memory Analyzer it’s :

Tool that can be used to analyze the memory usage of Java applications. It can be used to analyze heap dumps, which are snapshots of the memory state of a Java application at a specific point in time, as well as native memory dumps, core dumps, and thread dumps. MAT provides a number of features for analyzing and understanding the memory usage of Java applications, including:

  • Heap analysis: MAT can be used to analyze heap dumps and identify memory leaks, high memory usage, and other memory-related issues.
  • Dominator Tree: MAT provides a Dominator Tree view which can be used to see the objects that are preventing other objects from being garbage collected, and to identify the source of memory leaks.
  • Histogram: MAT provides a histogram view which can be used to see the number of instances of each class in the heap dump.
  • Query language: MAT provides a query language called OQL (Object Query Language) which allows you to write complex queries to extract information from heap dump.
  • Plugins: MAT can be integrated with other Eclipse plug-ins such as the Java Development Tools (JDT) for Java development and the Plug-in Development Environment (PDE) for plug-in development.

MAT is a powerful and useful tool for analyzing the memory usage of Java applications and troubleshooting memory-related issues.

As we can the object org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper is taking more than 95% of the heap memory.

The org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper class is an internal class in the Apache Tomcat library that represents a socket connection between a client and a server. It is used by the Tomcat NIO (Non-Blocking I/O) endpoint to manage and handle incoming socket connections.

Each NioSocketWrapper object represents a single socket connection, and it contains information about the connection such as the client IP address, the input and output streams, and various other state information. The NioEndpoint uses a pool of these NioSocketWrapper objects to handle incoming connections, and each NioSocketWrapper is associated with a worker thread that handles reading and writing data to the client.

In the context of a Spring Boot application, the NioSocketWrapper objects are created and managed by the embedded Tomcat container, and are used to manage the socket connections between the client and the application. The NioSocketWrapper objects are passed to the servlet engine, which in turn passes them to the application code for processing.

How Http request is handled by tomcat

  1. A user initiates an HTTPS request to the testHttpPostRequest() endpoint using the TLS protocol. This request is received by the NIO endpoint, which is responsible for accepting incoming connections (is 10000 conx his means that the maximum number of simultaneous connections that the connector can handle by default is 10000, unless this value is explicitly overridden in the configuration).
  2. The NIO endpoint creates a new socket for the incoming connection and assigns it to a worker thread from the thread pool. The number of threads in the pool is determined by the maxThreads parameter in the server.xml configuration file. By default, this value is set to 200.
  3. The worker thread handles the SSL handshake with the client. This involves verifying the client’s identity, establishing an encrypted connection, and negotiating the cipher suite and protocol versions to be used.
  4. Once the SSL handshake is complete, the worker thread reads the incoming HTTP request from the client and passes it to the appropriate Servlet container, in this case, the Spring Boot application(the Servlet container is embedded within the application itself. The embedded container is responsible for processing incoming HTTP requests and forwarding them to the appropriate controller method for processing. Spring Boot supports several embedded Servlet containers, including Tomcat, Jetty, and Undertow).
  5. The Servlet container uses the configured DispatcherServlet to route the request to the appropriate controller method, in this case, the testHttpPostRequest() method.
  6. The controller method processes the request, in this case, by printing the employee’s name and badge number to the console.
  7. The controller method sends a response back to the client with a status code of 200 (OK) and a message body containing the text « Hello world ». The worker thread handles the encryption and sends the response back to the client over the encrypted connection.
  8. Once the response has been sent, the worker thread returns to the thread pool, where it is available to handle another incoming connection.

How can i protect my application from OOM ?

There are several things you can do to protect your Spring Boot application from running out of memory:

  1. Optimize memory usage: Review your application code and ensure that you are not creating unnecessary objects or retaining objects longer than needed. You can also consider using memory profiling tools like JProfiler, Java Flight Recorder, or YourKit to analyze your application’s memory usage and identify areas for optimization.
  2. Tune the JVM parameters: You can adjust the JVM parameters to allocate more memory to your application and control how the garbage collector manages memory. For example, you can increase the heap size using the -Xmx and -Xms parameters, or adjust the garbage collection strategy using the -XX:+UseConcMarkSweepGC or -XX:+UseG1GC flags.
  3. Limit concurrent requests: If your application is handling a large number of concurrent requests, consider using a load balancer or implementing rate limiting to prevent overload and ensure that your application can handle the load.
  4. Implement caching: Consider implementing caching for frequently accessed data to reduce the number of requests and improve performance.
  5. Monitor and analyze performance: Use monitoring and analytics tools like Prometheus or Grafana to track your application’s performance metrics, including memory usage, CPU usage, and request latency. This will help you identify issues early and take proactive measures to prevent outages.
  6. Upgrade hardware: If your application is running on low-end hardware, consider upgrading to more powerful hardware to provide more memory and processing power.

By implementing these measures, you can help protect your Spring Boot application from running out of memory and ensure that it can handle increased traffic and load.

Laisser un commentaire