The interviewer of Gaode asked me: Can the service still run after the JVM memory overflows?

Let's ask a question at the beginning of the article, a java program, if OOM occurs in one of the threads, can other threads in the process still run?

Next, do the experiment to see if the program can still be accessed after the six kinds of OOM of the JVM.

Here I am using a springboot program.

/**
 * @author :charon
 * @date :Created in 2021/5/17 8:30
 * @description : 程序启动类
 * @version: 1.0
 */
@SpringBootApplication
public class CharonApplication {

    public static void main(String[] args) {
        SpringApplication.run(CharonApplication.class, args);
    }

}

Check whether the service is available ( http://localhost:8080/checkHealth  test service is available normally):

/**
 * @author :charon
 * @date :Created in 2021/5/17 8:49
 * @description : 测试服务是否可用
 * @version: 1.0
 */
@RestController
public class CheckHealthController {

    @RequestMapping("/checkHealth")
    public String stackOverFlowError(){
        System.out.println("调用服务监测接口-----------------------");
        return "服务监测接口返回";
    }
}

1.StackOverflowError (stack overflow)

A stack overflow means that an error occurs when the depth of the stack exceeds the stack size allocated to the thread by the virtual machine.

/**
 * @author :charon
 * @date :Created in 2021/5/17 8:49
 * @description : 测试java.lang.StackOverflowError: null的错误
 * @version: 1.0
 */
@RestController
public class StackOverFlowErrorController {

	/**
	 * 递归调用一个方法,使其超过栈的最大深度
	 */
    @RequestMapping("/stackOverFlowError")
    public void stackOverFlowError(){
        stackOverFlowError();
    }
}

Use the browser to call the stack overflow interface (localhost:8080/stackOverFlowError), and find that a stack overflow error is reported in the background.

image

Call the interface available to the monitoring program and find that it can still be accessed normally.

image

2. Java heap space (heap memory overflow)

When the GC is repeated many times, the heap memory of the new generation and the old generation is almost full, and the Full GC (Ergonomics) is frequently triggered until there is no memory space for the new objects. So the JVM threw an out-of-memory error! This causes the program to crash.

Set the virtual machine parameters (-Xms10m -Xmx10m -XX:+PrintGCDetails), if not set, it may be executed for a long time.

@RestController
public class JavaHeapSpaceController {

    /**
     * 使用是循环创建对象,是堆内存溢出
     */
    @RequestMapping("/javaHeapSpace")
    public void javaHeapSpace(){
        String str = "hello world";
        while (true){
            str += new Random().nextInt(1111111111) + new Random().nextInt(222222222);
            /**
             *  intern()方法:
             * (1)当常量池中不存在这个字符串的引用,将这个对象的引用加入常量池,返回这个对象的引用。
             * (2)当常量池中存在这个字符串的引用,返回这个对象的引用;
             */
            str.intern();
        }
    }
}

image

Call the interface available to the monitoring program and find that it can still be accessed normally.

image

3.direct buffer memory

When writing IO programs (such as Netty), ByteBuffer is often used to read or write data. This is an IO method based on channels and buffers. He can use the Native function library to directly allocate external memory, and then operate as a reference to this memory through a DirectByteBuffer object stored in the java heap, which can significantly improve performance in some scenarios, because it avoids copying data back and forth between the java heap and the native heap.

ByteBuffer.allocate(capacity) This method allocates jvm heap memory, which is under the jurisdiction of GC. It is slower due to the need to copy.
ByteBuffer.allocateDirect(capacity) This method allocates local memory and does not belong to the jurisdiction of GC. No memory copy is required, so it is faster

However, if the local memory is continuously allocated and the heap memory is rarely used, then the JVM does not need to perform GC, and the DirectByteBuffer object will not be recycled.
At this time, the heap memory is sufficient, but the local memory may have been used up. If you try to allocate the local memory again, the OutOfMemoryError will appear

Set JVM parameters: -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

@RestController
public class DirectBufferMemoryController {

    @RequestMapping("/directBufferMemory")
    public void directBufferMemory(){
        System.out.println("初始配置的最大本地内存为:"+ (sun.misc.VM.maxDirectMemory()/1024/1024)+"MB");
        // 在jvm参数里设置的最大内存为5M,
        ByteBuffer buffer = ByteBuffer.allocateDirect(6*1024*1024);
    }

}

Access the memory overflow interface ( http://localhost:8080/directBufferMemory), visit the service monitoring interface again after reporting an error, and find that you can continue to access.

image

4.GC overhead limit exceeded

Too long between GC collections will throw this error. The definition of too long is: more than 98% of the time is used for garbage collection and only less than 2% of the heap memory is recovered, and only less than 2% of the heap memory is recovered for multiple consecutive GCs. It will be thrown only in extreme cases of 2%. If the GC overhead limit error is not thrown, the following situations will occur:

  • The memory cleaned up by the GC will soon be filled up again, forming a vicious circle
  • CPU usage is always 100% and GC has no effect

Set JVM parameters: -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

@RestController
public class GcOverHeadController {

    @RequestMapping("/gcOverHead")
    public void gcOverHead(){
        int i = 0;
        List<String> list = new ArrayList<>();
        try{
            while(true){
                list.add(String.valueOf(++i).intern());
            }
        }catch(Throwable e){
            System.out.println("i的值为:" + i);
            e.printStackTrace();
            throw e;
        }
    }
}

As shown in the figure below, before the error is reported, in the frequent Full GC, but before and after garbage collection, the memory of the new generation and the old generation is similar, which means that the garbage collection has little effect.

image

Access the service monitoring interface again and find that you can continue to access.

5.Metaspace

In java 8 and later versions, MetaSpace is used instead of the permanent generation. The biggest difference between it and the permanent generation is:

MetaSpace is not in the virtual machine memory, but uses local memory, that is, in java8, Class metadata is stored in the native Memory of MetaSpace

The following information is stored in MetaSpace:

  • Class information loaded by the virtual machine
  • constant pool
  • static variable
  • Just-in-time compiled code

Parameter setting: -XX:+PrintGCDetails -XX:MetaspaceSize=50m -XX:MaxMetaspaceSize=50m

@RestController
public class MetaSpaceController {

    static class OomTest{

    }

    /**
     * 模拟MetaSpace溢出,不断生成类往元空间放,类占据的空间会超过MetaSpace指定的大小
     */
    @RequestMapping("/metaSpace")
    public void metaSpace(){
        int i = 0;
        try{
            while (true){
                i++;
                /**
                 * Enhancer允许为非接口类型创建一个java代理。Enhancer动态创建了给定类型的子类但是拦截了所有的方法,
                 * 和proxy不一样的是:不管是接口还是类它都能正常工作。
                 */
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(OomTest.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        return methodProxy.invokeSuper(o,objects);
                    }
                });
                enhancer.create();
            }
        }catch (Throwable e){
            System.out.println("i的值为:" + i);
            e.printStackTrace();
        }
    }
}

I remember reading an article on the official account before, which is the problem of Metaspace caused by the proxy class created by Fastjson. I also forgot the specific address. . . . .

image

Access the service monitoring interface again and find that you can continue to access.

6.unable to create new thread

In high concurrency services, the following errors often occur,

Cause:

  • 1. The application creates too many threads, and the threads created by an application process exceed the system load limit
  • 2. The server does not allow applications to create so many threads. The Linux system allows a single process to create 1024 threads by default (if it is an ordinary user, it is less than this value)

Solution:

  • 1. Reduce the number of threads created by the application, and analyze whether the application really needs to create so many threads
  • 2. For some applications that really need to create so many threads, you can modify the linux server configuration to expand the default limit of linux

View: ulimit -u

Modify: vim /etc/security/limits.d/90-nproc.conf

@RestController
public class UnableCreateThreadController {
	/**
     * 友情提示:千万别在windows中运行这段代码,如果不小心和我一样试了,那就只能强制重启了
     */
    @RequestMapping("/unableCreateThread")
    public void unableCreateThread(){
        for (int i = 0; ; i++) {
            System.out.println("i的值为:" + i);
            new Thread(()->{
               try{
                   Thread.sleep(1000*1000);
               } catch (InterruptedException e){
                   e.printStackTrace();
               }
            }).start();
        }
    }
}

I tested it with the root user and created 7409 threads. When everyone is testing, it is best to use ordinary user testing.
image

Finally, the interface of the detection service is executed, and it is found that the program can still be accessed.

summary

In fact, the thread in which OOM occurs will generally die, that is, it will be terminated, and the heap occupied by the object held by the thread will be occupied by the GC, and the memory will be released. Because gc is performed before OOM occurs, even if other threads can work normally, it will have a greater impact due to frequent gc.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324126969&siteId=291194637