循环日志

0 不允许在循环内打日志

 

1

主要策略:限制某个循环体内打日志的次数

主要困难:调用链太长,传递循环当前次数要改很多方法,类似于事务的connction,这种不方便传递参数的东西就想到了threadlocal

 

2 不想永久某线程到100就一直不让打了,想第二天的job清0

同时也不想用timer——threadlocal对象可析构——决定了要使用非static threadlocal对象——且threadlocal对象所在的bean不能单例,因为单例在ioc中往往不会释放,同时多例也有利于同一个线程处理不同业务的loop时能两边都打印,因为bean很可能不同,threadlocal不同,像(mybatis Guice 事务源码解析

 

 

3 多例是否能真正解决问题?

3.1 多例在ioc的本质

定义:对一个bean的每次请求创建一个新实例

疑问:什么叫请求,a)装配时 b)调用bean的某个方法时

结论:请求指装配时,在将b塞入A的对象时即已确定,没有方法能让两次调用xxx的b对象是不同对象

证明:

Class A {

  @Inject B b;

  func main() {

    for() {

      handleitem() {

    }

  }

  func handleitem() {

    // print b hashcode here, all same, so ioc is lie

    b.xxx();

    // print b hashcode here again

    b.xxx();

  }

}

 

3.2 我们的场景

job today -

A a1 = getClass(A.class);

a1.main(); 

in a1:

class A 

  inject B b1 - in B, inject limit1

  Inject C c1 - in C, Inject limit2

 

  main() {

    new Thread [] {

      b.xxx();c.xxx(); both b and c will print top N because different threadLocal object

    }

  }

}

a1/b1/c1/limit1/limit2 all get released during gc

 

job tomorrow -

A a2 = getClass(A.class);  get another A object

in a2

  inject B b2 - in B, inject limit3

  Inject C c2 - in C, Inject limit4

a2.main(), in this call, everything is new

 

要想threadlocal被释放,threadlocal所在bean要能释放,进而装配该bean的调用源头以多例的形式来调用,

在我们这里,是loader = guice.getClass(Loader),那么就要保证loader指向的那个对象能被回收,而不是被工厂保存起来

 

 4 代码

 

 

 5 测试

 调用链1:可以看到多线程各自计数

 调用链1的threadlocal被回收,调用链2的threadlocal被创建并回收

 再次调用链2,threadlocal被回收,从侧面证明了gui调用链也是多例

 LoggerCounter 7个没回收,预期内:ThreadLocal内存泄漏问题实践(二)

 第一次gc后,CounterThreadLocal对象还在,因为我们override了finalize方法,所以没有导致回收,只是重新加入回收队列,第2次gc后回收:java finalize及实践

 

 

5 tiny修改20241125

 

 

6 性能

一亿次调用printLog()

 

qps(亿)

1: 1.04

10: 1.86

100:1.91

200: 1.98

 

业务端流量为每秒10000 << 2yi

组合qps= 10000/(1+10000*1/2yi)=1/(1/10000+1/2yi)

 

 

 

7 用完了次数后,能不能显式把threadlocal=null,这样threadlocalmap就能直接空出来,不用依赖gc

不能,因为只是这个线程用完了

同时不用担心map的容量而提前手动回收,jdk会扩容,且200个threadlocal对象的性能跟1个也差不多

 

8 假如我就要一个线程达到limit后就停止,不顾其他线程了,要不要volatile修饰threadlocal对象?

1)当第一条线程 threadlocal=new endthreadlocal后,第二条线程getthreadlocal().get(),threadlocal理论上应该有延迟,另外一条线程的副本指到原本的threadlocal对象,这条强引用链是有可能存在一段时间的,如果考虑高精度时效,理论上可以用volatile强制刷新

2)threadlocal变量要不要volatile

 

 

9 test case

 

posted on 2024-11-01 21:31  silyvin  阅读(19)  评论(0)    收藏  举报