精华内容
下载资源
问答
  • Log4j blocked 线程阻塞问题
    千次阅读
    2020-08-29 16:40:40

     

    我的zoa项目中,一直使用的是log4j 1.x版本,由于简单,一直没更换,也没怀疑过它的问题。

     

    偶尔有两次,zoa在使用过程中假死的情况,不能响应任何请求,包括正常的关闭都无法进行

     

    于是我使用 jvm工具分析了JVM的情况,最终定位为 log4j引起的线程 blocked,不算是死锁,但是所以请求都会调用log4j,而log4j又无法正常执行,所以 所有请求都会 block。线程堆栈信息片段 形如:

    "qtp1056944384-232" prio=10 tid=0x00007f54900d0800 nid=0x63b3 waiting for monitor entry [0x00007f54492d0000]

       java.lang.Thread.State: BLOCKED (on object monitor)

        at org.apache.log4j.Category.callAppenders(Category.java:205)

        - waiting to lock <0x00000007e81c4830> (a org.apache.log4j.spi.RootLogger)

        at org.apache.log4j.Category.forcedLog(Category.java:391)

        at org.apache.log4j.Category.log(Category.java:856)

        at org.slf4j.impl.Log4jLoggerAdapter.info(Log4jLoggerAdapter.java:368)

    主要是下面这个类中的这段代码造成的:

     /**

      * Call the appenders in the hierrachy starting at <code>this</code>. If no

      * appenders could be found, emit a warning.

     */

    public void callAppenders(LoggingEvent event) {

     int writes = 0;

     

     for (Category c = this; c != null; c = c.parent) {

            // Protected against simultaneous call to addAppender,

            // removeAppender,...

            synchronized (c) {

                   if (c.aai != null) {

                         writes += c.aai.appendLoopOnAppenders(event);

                   }

                   if (!c.additive) {

                         break;

                   }

            }

     }

     

     if (writes == 0) {

            repository.emitNoAppenderWarning(this);

     }

    }

    它使用了synchronized(this),所有线程共用一个Category,而它通过log4j.properties指定。 同一个Category下的线程打log时,需要进行全局同步,因此它的效率会很低,不适合高并发的场景。

    详细分析参见:

    https://dzone.com/articles/log4j-thread-deadlock-case

     

    更多相关内容
  • 程序员的成长之路互联网/程序员/技术/资料共享关注阅读本文大概需要 2.8 分钟。来自:cnblogs.com/yeyang/p/10400486.html背景在使用log4j2打日志时...

    程序员的成长之路

    互联网/程序员/技术/资料共享 

    关注

    阅读本文大概需要 2.8 分钟。

    来自:cnblogs.com/yeyang/p/10400486.html

    背景

    在使用log4j2打日志时,当发生大量异常时,造成大量线程block问题的问题。

    一个关于log4j2的高并发问题:

    https://blog.fliaping.com/a-high-concurrency-problem-of-log4j2/

    大量线程block原因

    发生异常,打印异常栈时,会调用org.apache.logging.log4j.core.impl.ThrowableProxy.toExtendedStackTrace方法。

    ThrowableProxy.toExtendedStackTrace内部会进行loadClass操作。

    并且可以看到ClassLoader的loadClass在加载类时

    • 首先会持有锁。

    • 调用findLoadedClass看下是否类已经被加载过了

    • 如果类没被加载过,根据双亲委派模型去加载类。

    可以看到当某个类被加载过了,调用findLoadedClass会直接返回,锁也会被很快释放掉,无需经过双亲委派等后面的一系列步骤。

    但是,在进行反射调用时,JVM会进行优化,会动态生成名为sun.reflect.GeneratedMethodAccessor<N>的类,这个类无法通过ClassLoader.loadClass方法加载(为什么无法通过ClassLoader.loadClass加载?因为JVM内部自定义一个加载器DelegatingClassLoader来加载这个类,这导致应用类加载器 Launcher$AppClassLoader找不到它)。

    导致每次解析异常栈进行类加载时,锁占有的时间很长,最终导致阻塞。

    关于JVM对反射调用的优化

    Java中对反射的优化

    使用反射调用某个类的方法,jvm内部有两种方式

    • JNI:使用native方法进行反射操作。

    • pure-Java:生成bytecode进行反射操作,即生成类sun.reflect.GeneratedMethodAccessor<N>,它是一个被反射调用方法的包装类,代理不同的方法,类后缀序号会递增。这种方式第一次调用速度较慢,较之第一种会慢3-4倍,但是多次调用后速度会提升20倍

    对于使用JNI的方式,因为每次都要调用native方法再返回,速度会比较慢。所以,当一个方法被反射调用的次数超过一定次数(默认15次)时,JVM内部会进行优化,使用第2种方法,来加快运行速度。

    JVM有两个参数来控制这种优化

    • -Dsun.reflect.inflationThreshold=<value>value默认为15,即反射调用某个方法15次后,会由JNI的方式变为pure-java的方式

    • -Dsun.reflect.noInflation=true默认为false。当设置为true时,表示在第一次反射调用时,就转为pure-java的方式

    关于如何验证上面所说的反射优化以及两个参数的具体作用,可以参考R大的这篇博客

    https://rednaxelafx.iteye.com/blog/548536

    下面是一个验证反射优化的样例:

    public class TestMethodInvoke {
        public static void main(String[] args) throws Exception {
            Class<?> clz = Class.forName("A");
            Object o = clz.newInstance();
            Method m = clz.getMethod("foo", String.class);
            for (int i = 0; i < 100; i++) {
                m.invoke(o, Integer.toString(i));
            }
        }
    }
    
    
    public class A {
        public void foo(String name) {
            System.out.println("Hello, " + name);
        }
    }
    
    

    配置如下JVM参数,使得在第一次反射调用时,就转为pure-java的方式

    打断点跟踪:

    可以看到GeneratedMethodAccessor1的classLoader为DelegatingClassLoader,其parent为AppClassLoader。

    如何关闭JVM对反射调用的优化?

    想关闭JVM对反射优化怎么办?

    JVM中只提供了两个参数,因此,没有办法完全关闭反射优化。

    一种能想到的接近于关闭反射优化的方法就是将inflationThreshold设为的一个特别大的数。

    inflationThreshold是java中的int型值,可以考虑把其设置为Integer.MAX_VALUE ((2^31)-1)。

    $ java -Dsun.reflect.inflationThreshold=2147483647 MyApp
    
    

    <END>

    推荐阅读:

    【154期】面试官:你能说说 Elasticsearch 查询数据的工作原理是什么吗?

    【153期】面试官:谈谈常用的Arraylist和Linkedlist的区别

    【152期】面试官:你能说出MySQL主从复制的几种复制方式吗?

    5T技术资源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,单片机,树莓派,等等。在公众号内回复「2048」,即可免费获取!!

    微信扫描二维码,关注我的公众号

    朕已阅 

    展开全文
  • Log4j2阻塞业务线程引发的思考

    千次阅读 2019-04-27 18:59:01
    异步日志打印在ringbuffer满了之后2.7版本的log4j2会默认使用当前线程进行打印日志。 即使不使用默认的策略,2.9之后已经改为默认的为enqueue方式,也会因为最后队列的打满导致cpu飙高导致业务线程卡顿,2.7中队列...

    问题描述

    问题1:线上日志打印过多导致的业务线程阻塞

    1. 异步日志打印在ringbuffer满了之后2.7版本的log4j2会默认使用当前线程进行打印日志。
    2. 即使不使用默认的策略,2.9之后已经改为默认的为enqueue方式,也会因为最后队列的打满导致cpu飙高导致业务线程卡顿,2.7中队列使用offer提交日志事件,所以会阻塞
    3. 详细的原因2.7的版本博主已经有文章讲述,此处不再做过多赘述(https://blog.csdn.net/u010597819/article/details/87879802)

    问题2:异常线程栈打印使用讨论

    首先上官方讨论连接:https://issues.apache.org/jira/browse/LOG4J2-2391

    1. 异常线程栈的打印导致出现了大量的日志线程出现在load class时的锁阻塞
    2. 官网讨论中也指明了ThrowableProxy使用了不正确的CCL(ContextClassLoader)

    下面我们分析一下问题的原因

    ThrowableProxy使用错误的CCL原因分析

    日志详细流程不再赘述,直接从Appender追加日志梳理

    /**
     * Actual writing occurs here.
     *
     * @param logEvent The LogEvent.
     */
    @Override
    public void append(final LogEvent logEvent) {
        if (!isStarted()) {
            throw new IllegalStateException("AsyncAppender " + getName() + " is not active");
        }
        if (!Constants.FORMAT_MESSAGES_IN_BACKGROUND) { // LOG4J2-898: user may choose
            logEvent.getMessage().getFormattedMessage(); // LOG4J2-763: ask message to freeze parameters
        }
        final Log4jLogEvent memento = Log4jLogEvent.createMemento(logEvent, includeLocation);
        if (!transfer(memento)) {
            if (blocking) {
                // delegate to the event router (which may discard, enqueue and block, or log in current thread)
                final EventRoute route = asyncQueueFullPolicy.getRoute(thread.getId(), memento.getLevel());
                route.logMessage(this, memento);
            } else {
                error("Appender " + getName() + " is unable to write primary appenders. queue is full");
                logToErrorAppenderIfNecessary(false, memento);
            }
        }
    }
    

    异步Appender追加日志

    1. 异步Appender追加日志AsyncAppender.append
    2. 如果不是异步格式化日志
    3. 根据日志事件LogEvent创建Log4jLogEvent
    4. 将Log4jLogEvent尝试提交至队列,如果是TransferQueue类型则尝试转换,否则offer提交至默认的blockingQueue阻塞队列
    5. 如果提交队列失败(队列满了或者其他种种原因)
    6. 如果是阻塞类型的Appender则提交给EventRout路由处理日志事件
    7. 否则通知异常handle句柄并打印error日志如果存在errorAppender

    创建log4j日志事件

    1. Log4jLogEvent根据日志事件Log4jEvent copy并创建一个final类型的日志对象
    2. Log4jLogEvent序列化日志事件Log4jEvent返回一个日志事件代理LogEventProxy
    3. 如果日志事件是Log4jLogEvent类型
    4. 调用事件getThrownProxy方法确认ThrownProxy已经完成初始化,如果thrownProxy为空则根据Thrown创建thrown代理
    5. 创建代理并返回
    6. Log4jLogEvent根据序列化对象将其反序列化为Log4jLogEvent对象

    创建ThrownProxy代理

    private ThrowableProxy(final Throwable throwable, final Set<Throwable> visited) {
        this.throwable = throwable;
        this.name = throwable.getClass().getName();
        this.message = throwable.getMessage();
        this.localizedMessage = throwable.getLocalizedMessage();
        final Map<String, CacheEntry> map = new HashMap<>();
        final Stack<Class<?>> stack = ReflectionUtil.getCurrentStackTrace();
        this.extendedStackTrace = this.toExtendedStackTrace(stack, map, null, throwable.getStackTrace());
        final Throwable throwableCause = throwable.getCause();
        final Set<Throwable> causeVisited = new HashSet<>(1);
        this.causeProxy = throwableCause == null ? null : new ThrowableProxy(throwable, stack, map, throwableCause,
            visited, causeVisited);
        this.suppressedProxies = this.toSuppressedProxies(throwable, visited);
    }
    
    1. 根据阻塞的堆栈我们可以看到日志阻塞点,我们直奔主题,查看获取扩展堆栈信息的代码toExtendedStackTrace
    2. 判断throwable堆栈是否与当前堆栈类名相同,是则使用当前堆栈中class类的CL(classloader)作为lastLoader,使用当前堆栈创建扩展堆栈信息并缓存至extendedStackTrace
    3. 如果类名与当前堆栈类不同则根据类名从map临时缓存中获取缓存CacheEntry,根据缓存创建扩展堆栈信息及更相信lastLoader
    4. 否则使用lastLoader按照类名称加载class类,再根据class类获取类位置以及版本信息,如果获取不到则使用符号:‘?’代替,例如:
    at sun.reflect.GeneratedMethodAccessor321.invoke(Unknown Source) ~[?:?]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_77]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_77]
    at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:216) ~[spring-core-4.3.15.RELEASE.jar!/:4.3.15.RELEASE]
    at org.springframework.cloud.context.scope.GenericScope$LockedScopedProxyFactoryBean.invoke(GenericScope.java:472) ~[spring-cloud-context-1.3.3.RELEASE.jar!/:1.3.3.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.15.RELEASE.jar!/:4.3.15.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) ~[spring-aop-4.3.15.RELEASE.jar!/:4.3.15.RELEASE]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [?:1.8.0_77]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [?:1.8.0_77]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_77]
    
    1. 而产生大量锁阻塞的地方就是loadClass部分,根据进程堆栈中的锁可以看到正是ClassLoader的锁位置
    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
                ...
        }
    }
    
    1. 产生锁竞争的原因是因为class名称相同,那么相同的类名称为什么会加载多次呢?

    为什么同一个类会加载多次?

    原因大家应该很容易猜到,在不同的classloader中加载同一个类多次是没毛病的。那么我们进一步分析是解析哪个class时出现了lastLoader找不到的情况。断点日志查看是这家伙GeneratedMethodAccessor321

    GeneratedMethodAccessor类

    1. 通过搜索果然根本找不到这个类,于是查询了一下资料,是JVM对反射调用的优化策略产生的类
    2. 如果设置的不膨胀并且不是VM匿名类,则直接怼反射进行生成字节码的方式调用
    3. 否则创建代理访问反射方法进行调用。在调用次数超过阈值(默认15)时(即发生膨胀)。对反射方法生成字节码并以后采用该方式进行调用
    public MethodAccessor newMethodAccessor(Method var1) {
        checkInitted();
      //不膨胀,直接生成字节码方式调用(并且不是VM匿名类)
        if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
            return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
        } else {
            NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
            DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
            var2.setParent(var3);
            return var3;
        }
    }
    //NativeMethodAccessorImpl
    public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
        //如果调用次数发生膨胀超过阈值,并且不是VM匿名类,生成字节码方式调用
        if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
            MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
            this.parent.setDelegate(var3);
        }
        //否则反射调用
        return invoke0(this.method, var1, var2);
    }
    
    1. 继续查看生成的字节码是如果加载的MethodAccessorGenerator.generateMethod
    2. 可以看到一堆ASM字节码生成器的代码拼装。最后可以看到使用的var1参数的classloader进行的加载,也就是方法的声明类
    //入参var1是反射调用的方法method的声明类
    (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
    private MagicAccessorImpl generate(final Class<?> var1, String var2, Class<?>[] var3, Class<?> var4, Class<?>[] var5, int var6, boolean var7, boolean var8, Class<?> var9) {
        ByteVector var10 = ByteVectorFactory.create();
        this.asm = new ClassFileAssembler(var10);
        ...
            return (MagicAccessorImpl)AccessController.doPrivileged(new PrivilegedAction<MagicAccessorImpl>() {
                public MagicAccessorImpl run() {
                    try {
                      //使用ClassDefiner声明类,最后一个参数是使用的var1的classloader,也就是反射方法声明类的classloader
                        return (MagicAccessorImpl)ClassDefiner.defineClass(var13, var17, 0, var17.length, var1.getClassLoader()).newInstance();
                    } catch (IllegalAccessException | InstantiationException var2) {
                        throw new InternalError(var2);
                    }
                }
            });
        }
    }
    class ClassDefiner {
        static final Unsafe unsafe = Unsafe.getUnsafe();
        static Class<?> defineClass(String var0, byte[] var1, int var2, int var3, final ClassLoader var4) {
          //DelegatingClassLoader代理classloader直接委派原classloader加载
          //即:使用声明方法类的classloader加载
            ClassLoader var5 = (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
                public ClassLoader run() {
                    return new DelegatingClassLoader(var4);
                }
            });
            return unsafe.defineClass(var0, var1, var2, var3, var5, (ProtectionDomain)null);
        }
    }
    
    1. 那么如果lastLoader也就是堆栈的上一层的classloader与使用反射调用的方法声明类的classloader不一致就会产生每次出现该异常就会重新加载该类,如果大量的该种情况处的异常出现,则会造成极大的性能损耗。

    问题总结

    问题1

    该问题可以选择适宜的策略来进行规避,比如使用Discard模式丢弃队列满或者消费繁忙时的日志,并且重写日志队列,取消队列阻塞方式的offer添加

    问题2

    这类问题官方的讨论中也有开发者给出了感叹:除了允许禁用扩展堆栈跟踪信息,或者牺牲多个类加载器存在时的正确性之外,我不确定我们还能做什么。哈哈image.png

    展开全文
  • 在使用log4j2打日志时,当发生大量异常时,造成大量线程block问题的问题。 一个关于log4j2的高并发问题:https://blog.fliaping.com/a-high-concurrency-problem-of-log4j2/ 大量线程block原因 发生异常,打印...

    目录

    背景

    大量线程block原因

    关于JVM对反射调用的优化

    如何关闭JVM对反射调用的优化?

    参考资料


    背景

    在使用log4j2打日志时,当发生大量异常时,造成大量线程block问题的问题。

    一个关于log4j2的高并发问题:https://blog.fliaping.com/a-high-concurrency-problem-of-log4j2/

     

    大量线程block原因

    发生异常,打印异常栈时,会调用org.apache.logging.log4j.core.impl.ThrowableProxy.toExtendedStackTrace方法。

    ThrowableProxy.toExtendedStackTrace内部会进行loadClass操作。

    并且可以看到ClassLoader的loadClass在加载类时

    1)首先会持有锁。

    2)调用findLoadedClass看下是否类已经被加载过了

    3)如果类没被加载过,根据双亲委派模型去加载类。

    可以看到当某个类被加载过了,调用findLoadedClass会直接返回,锁也会被很快释放掉,无需经过双亲委派等后面的一系列步骤。

     

    但是,在进行反射调用时,JVM会进行优化,会动态生成名为sun.reflect.GeneratedMethodAccessor<N>的类,这个类无法通过ClassLoader.loadClass方法加载(为什么无法通过ClassLoader.loadClass加载?因为JVM内部自定义一个加载器DelegatingClassLoader来加载这个类,这导致应用类加载器 Launcher$AppClassLoader找不到它)。

    导致每次解析异常栈进行类加载时,锁占有的时间很长,最终导致阻塞。

     

    关于JVM对反射调用的优化

    Java中对反射的优化

    使用反射调用某个类的方法,jvm内部有两种方式

    1. JNI:使用native方法进行反射操作。

    2. pure-Java:生成bytecode进行反射操作,即生成类sun.reflect.GeneratedMethodAccessor<N>,它是一个被反射调用方法的包装类,代理不同的方法,类后缀序号会递增。这种方式第一次调用速度较慢,较之第一种会慢3-4倍,但是多次调用后速度会提升20倍

    对于使用JNI的方式,因为每次都要调用native方法再返回,速度会比较慢。所以,当一个方法被反射调用的次数超过一定次数(默认15次)时,JVM内部会进行优化,使用第2种方法,来加快运行速度。

    JVM有两个参数来控制这种优化

    -Dsun.reflect.inflationThreshold=<value>
    value默认为15,即反射调用某个方法15次后,会由JNI的方式变为pure-java的方式

    -Dsun.reflect.noInflation=true

    默认为false。当设置为true时,表示在第一次反射调用时,就转为pure-java的方式

    关于如何验证上面所说的反射优化以及两个参数的具体作用,可以参考R大的这篇博客https://rednaxelafx.iteye.com/blog/548536

     

    下面是一个验证反射优化的样例:

    public class TestMethodInvoke {
        public static void main(String[] args) throws Exception {
            Class<?> clz = Class.forName("A");
            Object o = clz.newInstance();
            Method m = clz.getMethod("foo", String.class);
            for (int i = 0; i < 100; i++) {
                m.invoke(o, Integer.toString(i));
            }
        }
    }

     

    public class A {
        public void foo(String name) {
            System.out.println("Hello, " + name);
        }
    }

     

    配置如下JVM参数,使得在第一次反射调用时,就转为pure-java的方式

    打断点跟踪:

    可以看到GeneratedMethodAccessor1的classLoader为DelegatingClassLoader,其parent为AppClassLoader。

     

     

    如何关闭JVM对反射调用的优化?

    想关闭JVM对反射优化怎么办?

    JVM中只提供了两个参数,因此,没有办法完全关闭反射优化。

    一种能想到的接近于关闭反射优化的方法就是将inflationThreshold设为的一个特别大的数。

    inflationThreshold是java中的int型值,可以考虑把其设置为Integer.MAX_VALUE ((2^31)-1)。

    $ java -Dsun.reflect.inflationThreshold=2147483647 MyApp

     

     

    参考资料

    https://rednaxelafx.iteye.com/blog/548536 R大的博客

    https://blogs.oracle.com/buck/inflation-system-properties

    展开全文
  • log4j引起阻塞的一个坑,见下图:
  • 线程死锁就是有两个线程,一个线程锁住了资源A,又想去锁定资源B,另外一个线程锁定了资源B,又想锁定资源A。两个线程都想去得到对方的资源,而不愿意释放自己的资源,从而造成一种相互等待,无法执行的情况。 这么...
  • 一、问题现象 ...使用Jstack导出java的线程信息如下: 2021-02-22 18:46:38 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.121-b13 mixed mode): "Attach Listener" #99 daemon prio=9 os_prio=.
  • 在使用log4j2打日志时,当发生大量异常时,造成大量线程block问题的问题。 大量线程block原因 发生异常,打印异常栈时,会调用org.apache.logging.log4j.core.impl.ThrowableProxy.toExtendedStackTrace方法。 ...
  • 一次鞭辟入里的 Log4j2 日志输出阻塞问题的定位 问题现象 线上某个应用的某个实例突然出现某些次请求服务响应极慢的情况,有几次请求超过 60s 才返回,并且通过日志发现,服务线程并没有做什么很重的操作。这种情况...
  • log4jlog4j2性能对比及log4j升级至log4j2方案

    万次阅读 多人点赞 2019-06-16 11:03:53
    之前某个服务在压测环境中出现了问题,分析之后得知是log4jLogger对象争用厉害,很多线程阻塞在此。 以上问题证明log4j在高并发高QPS情况下,是存在性能问题的。 之后把log4j升级成了log4j2,并采取异步日志模式...
  • Log4j 1.x版 引发线程blocked死锁问题

    千次阅读 2017-05-10 13:42:51
    Apache Log4j是一个基于Java的日志记录工具,用起来非常方便,但是Log4j 1.x如果使用不慎,会引起死锁问题,进行导致整个网站的宕机 示例如下: log4j 配置 &lt;?xml version="1.0" ...
  • 2. log4j简介和配置2.1 log4j介绍2.2 log4j三大组件2.3 log4j pom依赖引入2.4 配置log4j3. logback简介和配置3.1 logback简介3.2 logback pom依赖引入3.3 logback 配置模版3.4 logback总结4. logback和log4j对比5. ...
  • log4j导致的性能问题

    2021-03-08 17:24:22
    优质文章,第一时间送达上一篇:下一篇:作者 |Hello-YOYO来源 |http://suo.im/4Xczmo问题背景双十一零点时,有一个服务A(后文该服务都用A来代替)的tp99由平常的50ms左右突然彪到60000ms,导致调用端积累了几十W的...
  • 在之前的一篇文章中(一次鞭辟入里的 Log4j2 异步日志输出阻塞问题的定位),我们详细分析了一个经典的 Log4j2 异步日志阻塞问题的定位,主要原因还是日志文件写入慢了。并且比较深入的分析了 Log4j2 异步日志的原理,...
  • Log4j2详解

    2020-05-19 11:10:23
    Log4j 2中记录日志的方式有同步日志和异步日志两种方式,其中异步日志又可分为使用AsyncAppender和使用AsyncLogger两种方式。 2.Log4j2中的同步日志 所谓同步日志,即当输出日志时,必须等待日志输出语句执行完毕...
  • Log4j的异步日志

    2021-03-01 08:08:38
    先引用一些网络上的异步日志介绍log4j日志异步化大幅提升系统性能 - 穿林度水 - 博客园​www.cnblogs.comorg.apache.log4j.AsyncAppender就是实现异步日志输入的Appender我们知道,一个日志输出行为,牵涉到logger,...
  • 文章目录一、log4j.properties配置文件详解1.配置rootLogger2.配置日志信息输出目的地AppenderAppender的配置方式Appender的配置详解3.配置日志信息的格式(布局)4.log4j.properties配置文件示例二、log4j....
  • 文章目录前言环境log4j日志log4j 在 springboot 中的配置性能测试log4j2log4j2 异步日志AsyncAppender 和 Asynchronous Loggers全异步和混合异步总结彩蛋1-性能测试需要注意的点彩蛋2-springboot 中多环境日志配置...
  • Log4j2包含基于LMAX Disruptor库的下一代异步记录器,在多线程中,异步记录器的吞吐量比Log4j和Logback高18倍,且延迟低 自动重新加载配置 与Logback一样可以在修改时自动重新加载其配置,不同之处在于其重新配置...
  • log4j引起的性能问题

    2021-05-19 10:34:19
    配置: log4j.rootLogger=DEBUG,Console log4j.appender.Console=org.apache.log4j.ConsoleAppender log4j.appender.Console.Target=System.out log4j.appender.Console.Threshold=DEBUG log4j.appender.Console....
  • 1、log4j已成为大型系统必不可少的一部分,log4j可以很方便的帮助我们在程序的...2、log4j主要分为error,warn,info,debug个级别,也是使用最多的种,日志级别从左至右依次增加。3、log4j对系统性能的影响程度...
  • log4j2 最近,我们正在对流行的SaaS应用程序进行故障排除。 该应用程序间歇性地变慢。 要从问题中恢复,必须重新启动应用程序。 在高流量期间,此应用有时会变慢。 有时在交通繁忙时也是如此。 没有凝聚力模式。 ...
  • Log4j2最佳实践

    2021-06-07 07:51:36
    [TOC]1. 版本选择推荐使用log4j... pom依赖配置和升级方案pom配置org.slf4jslf4j-api1.7.24org.apache.logging.log4jlog4j-1.2-api2.11.0org.apache.logging.log4jlog4j-core2.11.0org.apache.logging.log4jlog4j-ap...
  • Java日志体系(log4j)

    2021-01-12 13:43:01
    log4j1.1 简介Log4j是一个由Java编写可靠、灵活的日志框架,是Apache旗下的一个开源项目;现如今,Log4j已经被移植到了C、C++、Python等语言中,服务更多的Developer;使用Log4j,我们更加方便的记录了日志信息,它...
  • 先回答主要问题:到底是io瓶颈,还是锁竞争导致线程block?死锁是显而易见的,一是因为它会一直阻塞而系统load很低,二是也很容易通过jstack监测到死锁。另外一个问题:另外我对他文末提供的关于每个调用处static 一...
  • Java日志体系简介log4j架构支持对象核心对象Log4j中有三个主要组成部分Logger:日志对象,负责捕捉日志记录信息Appender:日志输出目的地,负责把格式好的日志信息输出到指定地方,可以是控制台、磁盘文件等Layout:...
  • log4j2日志打印、滚动切割流程解析
  • 为什么程序需要记录日志 我们不可能实时的24小时对系统进行人工监控,那么如果程序出现异常错误时要如何排查呢?并且系统在运行时做了哪些事情我们又从...日志框架出现的历史顺序为 : log4j → JUL → JCL → SLF4J

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,957
精华内容 10,382
关键字:

log4j线程阻塞