Lado de Meituan: después de OOM, ¿salirá definitivamente la JVM? ¿Por qué?

decir de frente

En la comunidad de lectores (más de 50 años) de Nien, un arquitecto veterano de 40 años , algunos amigos obtuvieron calificaciones de entrevistas recientemente para empresas de Internet de primer nivel como Meituan, Pinduoduo, Jitu, Youzan y Xiyin. Preguntas de entrevista muy importantes :

  • ¿Saldrá la JVM después de OOM? ¿Por qué?

En el entorno de producción, muchos socios pequeños también se han encontrado con el problema de que la JVM se cerrará después de OOM.

Aquí, Nien le dará una clasificación sistemática y sistemática, para que pueda demostrar completamente sus fuertes "músculos técnicos" y hacer que el entrevistador ame "no puede evitarlo, babeando" .

Esta pregunta y la respuesta de referencia también se incluyen en nuestra versión V81 " Nin Java Interview Collection ", para referencia de amigos posteriores, para mejorar el nivel de desarrollo, diseño y arquitectura 3-high de todos.

Para obtener los archivos PDF de las últimas "Notas de arquitectura de Nin", "Trilogía de alta concurrencia de Nin" y " Colección de entrevistas de Nin Java ", obténgalos a través de esta cuenta oficial [Technical Freedom Circle], contraseña: obtener el libro electrónico

fondo del problema

El problema es que a menudo decimos: cuando ocurre OOM, el programa se cuelga.

Muchas situaciones son: se produce OOM y la JVM no se bloquea.

Revisar OOM y excepciones

Echemos un vistazo a OutOfMemoryError.Después de todo, OutOfMemoryError es solo una excepción en Java.

OutOfMemoryError pertenece a la serie Error de excepciones no verificadas y su relación de herencia es la siguiente

Object
	Throwable
		Error
			VirtualMachineError
				OutOfMemoryError

Echemos un vistazo a la relación entre la memoria de almacenamiento dinámico insuficiente y las excepciones OutOfMemoryError.

Cuando se produce un OutOfMemoryError en un subproceso, en primer lugar, el espacio de almacenamiento dinámico no es suficiente y, a continuación, jvm genera una excepción OOM en la llamada al solicitar el espacio asignado.

El subproceso que se aplica a la memoria manejará OutOfMemoryError al igual que otras excepciones comunes.

El subproceso es la unidad básica de programación de recursos.Java considera completamente la independencia de los subprocesos al diseñar subprocesos.

En términos de excepciones, los subprocesos también mantienen la independencia de las excepciones de subprocesos.

En la ejecución de subprocesos, si se produce una excepción, el subproceso la manejará de forma independiente en lugar de enviarla a otros subprocesos. Esto es para asegurar la independencia de este hilo.

Desde la dimensión de implementación de hilos, también podemos ver la estrategia de manejo de excepciones.

En el subproceso Subproceso, el método de ejecución del objeto de destino interno finalmente se ejecutará, que es el método de implementación de la interfaz java.lang.Runnable. El subproceso se ejecuta a través de su método de ejecución. La firma del método es la siguiente:

public abstract void run();

Tenga en cuenta que con este método, el método de ejecución no puede declarar que se produzcan excepciones comprobadas. Por lo tanto, cualquier excepción verificada que ocurra durante la ejecución de un método de subproceso debe manejarse dentro del subproceso.

Cuando un subproceso obtiene una excepción, hay dos formas de solucionarlo:

  • La excepción es capturada y manejada, y el hilo continúa ejecutándose.
  • el hilo deja de ejecutarse

controlador de excepciones predeterminado

si no es atrapado

Además de las excepciones verificadas, también hay excepciones no verificadas en java, que pueden generarse en la cadena de llamadas al método sin una declaración explícita.

El subproceso proporciona un controlador de excepciones predeterminado para esta excepción no controlada:

/**
* Dispatch an uncaught exception to the handler. This method is
* intended to be called only by the JVM. (将未被捕获的异常分发给处理器。这个方法只被JVM调用)
*/
private void dispatchUncaughtException(Throwable e) {
    
    
   getUncaughtExceptionHandler().uncaughtException(this, e);
}

El subproceso del método init() de Thread tiene al menos un controlador de excepciones predeterminado. El controlador de excepciones de abajo hacia arriba es el grupo de subprocesos ThreadGroup del subproceso principal del subproceso actual. Puede ver que el grupo de subprocesos es capaz de manejar excepciones:

public  class ThreadGroup implements Thread.UncaughtExceptionHandler {
    
    }

A través de estos dos mecanismos, el subproceso garantiza que la excepción que se produce internamente se resuelva dentro del subproceso y no se lance al subproceso externo que inició el subproceso.

Condición de salida de JVM

La condición para que la máquina virtual Java salga es: no hay subprocesos que no sean demonios (subprocesos en primer plano) en la JVM, y la JVM se cerrará.

Una excepción no controlada en un subproceso (las excepciones no controladas son manejadas por el controlador de excepciones predeterminado) hará que el subproceso finalice y finalice el subproceso. Si hay subprocesos que no son daemon (subprocesos en primer plano), la JVM no se cerrará.

OOM también es una excepción y su aparición no hará que se cierre la JVM.

Por lo tanto, OOM no tiene una fuerte relación con la salida de JVM.

Los siguientes ejemplos ilustran:

Ejemplo 1 : subproceso OOM, JVM no necesariamente sale

Ejemplo 2 : grupo de subprocesos OOM, JVM no necesariamente sale

Ejemplo 1: subproceso OOM, JVM no necesariamente sale

Después de que el subproceso 0 arroje OOM y finalice el subproceso, el subproceso principal aún imprimirá "Estoy bien..." en un bucle.

Una excepción OOM ocurre en un subproceso, al igual que otras excepciones, excepto que el subproceso finaliza, pero no afecta a otros subprocesos.

thread-0 thread OOM, no hará que la JVM se cierre.

Ejemplo 2: grupo de subprocesos OOM, JVM no necesariamente sale

class OOMThreadPool {
    
    
    private final Byte[] toLeak;

    public OOMThreadPool() {
    
    
        toLeak = new Byte[1024 * 1024];
    }

    static final Thread[] t = new Thread[1];
    static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 5,
            TimeUnit.SECONDS, new ArrayBlockingQueue<>(9),

            new ThreadFactory() {
    
    
                public Thread newThread(Runnable r) {
    
    
                    t[0] = new Thread(r);
                    t[0].setDaemon(false);
                    t[0].setPriority(Thread.NORM_PRIORITY);
                    t[0].setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    
    
                        @Override
                        public void uncaughtException(Thread t, Throwable e) {
    
    
                            e.printStackTrace();
                            System.out.println(t.getName() + " 的状态:" + t.getState());
                            System.out.println("这里是没有捕获的处理 ====> " + t.getId() + "==> " + e.getLocalizedMessage());
                        }
                    });
                    return t[0];
                }
            },
            new ThreadPoolExecutor.DiscardOldestPolicy()) {
    
    


        @Override
        protected void afterExecute(Runnable r, Throwable t) {
    
    
            System.out.println(Thread.currentThread().getName() + " 任务执行完成,但是线程不会结束");
            if (null != t) {
    
    
                System.out.println(Thread.currentThread().getName() + "任务异常了");
                t.printStackTrace();

            }

        }
    };


    // 为快速发生oom,设置堆大小; VM args: -Xms10m -Xmx10m
    public static void main(String[] args) throws InterruptedException {
    
    
        List<OOMThreadPool> list = new LinkedList<>();

        Runnable target = () -> {
    
    
            System.out.println(Thread.currentThread().getName() + " 开始了");
            try {
    
    
                while (true) {
    
    
                    list.add(new OOMThreadPool());
                }
            }catch ( Throwable throwable)
            {
    
    
                throwable.printStackTrace();
            }

        };

        threadPool.submit(target);
        while (true) {
    
    
            System.out.println(Thread.currentThread().getName() + " 我还行...");
            System.out.println(t[0].getName() + " 的状态:" + t[0].getState());
            Thread.sleep(1000L);
        }
    }
}

En el grupo de subprocesos, después de que el subproceso 0 arroje OOM y finalice el subproceso, el subproceso principal aún imprimirá "Estoy bien..." en un bucle.

En el grupo de subprocesos, la tarea del subproceso 0 ha terminado, pero el subproceso no ha terminado y aún se pueden ejecutar nuevas tareas.

La relación entre OOM y la salida JVM

¿Cuándo ocurre OOM y sale JVM?

  • Escenario 1 : todos los subprocesos que no son daemon son OOM porque no pueden solicitar memoria, todos los subprocesos que no son daemon salen, JVM sale, esta es una salida voluntaria

La ocurrencia de OOM indica que la memoria del montón de JVM está agotada en este momento y no se pueden asignar más recursos, o la eficiencia de recuperación del GC no es considerable.

El OOM de un subproceso, bajo un cierto grado de concurrencia, si otros subprocesos (incluidos los subprocesos que no son daemon) también necesitan solicitar memoria de montón en este momento, entonces otros subprocesos también lo harán porque no pueden solicitar memoria, e incluso un la reacción en cadena hará que toda la JVM salga.

  • Escenario 2 : Desbordamiento de OOM, lo que indica que la memoria está agotada. Si la memoria del sistema operativo está agotada, se producirá un asesino de OOM (asesino de falta de memoria), que matará el proceso de JVM, lo que dará como resultado una salida pasiva

El kernel de Linux tiene un mecanismo llamado OOM killer (Out Of Memory killer), que monitorea aquellos procesos que ocupan demasiada memoria, especialmente aquellos procesos que ocupan memoria muy rápidamente, y luego elimina automáticamente el proceso para evitar el agotamiento de la memoria. El kernel detecta que la memoria del sistema es insuficiente, selecciona y elimina un determinado proceso. Consulte el código fuente del kernel linux/mm/oom_kill.c. Cuando la memoria del sistema es insuficiente, se activa out_of_memory() y luego llama a select_bad_process(). para seleccionar un proceso "malo" se elimina. ¿Cómo juzgar y seleccionar un proceso "malo"? Linux selecciona un proceso "malo" llamando a oom_badness(). El algoritmo de selección y la idea son muy simples y sencillos: el proceso más malo es el que ocupa más memoria.

decir al final

Las preguntas de entrevista relacionadas con OOM son preguntas de entrevista muy comunes.

Si puede responder las 5 soluciones principales anteriores con fluidez y familiaridad, básicamente el entrevistador se sorprenderá y se sentirá atraído por usted.

Al final, el entrevistador se enamoró tanto que "no pudo evitarlo, babeando" . La oferta está llegando.

Durante el proceso de aprendizaje, si tiene alguna pregunta, puede acudir al arquitecto Nien, de 40 años, para comunicarse.

Lectura recomendada

" Artículo bueno de lujo superior: palabras de 3W, penetrar el principio comercial de Spring, código fuente, leído al menos 10 veces "

" Tencent es demasiado despiadado: 4 mil millones de cuentas QQ, dada la memoria 1G, ¿cómo deduplicar? "

" Jingdong es demasiado despiadado: deduplicación de datos de 100 W, uso distinto o grupo por, ¿hablar sobre el motivo? "

" Meituan es demasiado despiadado: la interfaz se robó maliciosamente 10Wqps, ¿cómo evitarlo? "

La actualización del archivo PDF de las notas de arquitectura de Nien y las preguntas de la entrevista anteriores, ▼ Vaya a la siguiente cuenta oficial de [Technical Freedom Circle] para obtenerlo ▼

Supongo que te gusta

Origin blog.csdn.net/crazymakercircle/article/details/131264292
Recomendado
Clasificación