达内教育IT培训沈阳分校

试听课 + 活动课
填写信息优先获取试听课

位置:学校首页 > 学校动态>沈阳好的java开发培训学校

沈阳好的java开发培训学校

沈阳好的java开发培训学校,小编在这里为您推荐达内教育,凭借雄厚的技术研发实力、过硬的教学质量、成熟的就业服务团队,为学员提供强大的职业竞争力,在用人企业中树立了良好的口碑.为不影响您的学习,来校区前请先电话或QQ咨询,方便我校安排相关课程的专业老师为您解答~接下来达内教育的小编为您分享,Java中八个潜在的内存泄露风险,你知道几个?

7. ThreadLocal的误用

  ThreadLocal一定要列在Java内存泄露的榜首,总能在不知不觉中将内存泄露掉,一个常见的例子是:

  @Test

  public void testThreadLocalMemoryLeaks() {

      ThreadLocal<List<Integer>> localCache = new ThreadLocal<>();

     List<Integer> cacheInstance = new ArrayList<>(10000);

      localCache.set(cacheInstance);

      localCache = new ThreadLocal<>();

  }

  当localCache的值被重置之后cacheInstance被ThreadLocalMap中的value引用,无法被GC,但是其key对ThreadLocal实例的引用是一个弱引用,本来ThreadLocal的实例被localCache和ThreadLocalMap的key同时引用,但是当localCache的引用被重置之后,则ThreadLocal的实例只有ThreadLocalMap的key这样一个弱引用了,此时这个实例在GC的时候能够被清理。

  其实看过ThreadLocal源码的同学会知道,ThreadLocal本身对于key为null的Entity有自清理的过程,但是这个过程是依赖于后续对ThreadLocal的继续使用,假如上面的这段代码是处于一个购买场景下,会有一个瞬间的流量峰值,这个流量峰值也会将集群的内存打到高位(或者运气不好的话直接将集群内存打满导致故障),后面由于峰值流量已过,对ThreadLocal的调用也下降,会使得ThreadLocal的自清理能力下降,造成内存泄露。ThreadLocal的自清理实现是锦上添花,千万不要指望它雪中送碳。

  8. 类的静态变量

  Tomcat对在网络容器中使用ThreadLocal引起的内存泄露做了一个总结,详见:

  https://cwiki.apache.org/confluence/display/tomcat/MemoryLeakProtection,这里我们列举其中的一个例子。

  熟悉Tomcat的同学知道,Tomcat中的web应用由webapp classloader这个类加载器的,并且webapp classloader是破坏双亲委派机制实现的,即所有的web应用先由webapp classloader加载,这样的好处就是可以让同一个容器中的web应用以及依赖隔离。

  下面我们看具体的内存泄露的例子:

  public class MyCounter {

   private int count = 0;

   public void increment() {

    count++;

   }

   public int getCount() {

    return count;

   }

  }

  public class MyThreadLocal extends ThreadLocal<MyCounter> {

  }

  public class LeakingServlet extends HttpServlet {

   private static MyThreadLocal myThreadLocal = new MyThreadLocal();

   protected void doGet(HttpServletRequest request,

     HttpServletResponse response) throws ServletException, IOException {

    MyCounter counter = myThreadLocal.get();

    if (counter == null) {

     counter = new MyCounter();

     myThreadLocal.set(counter);

    }

    response.getWriter().println(

      "The current thread served this servlet " + counter.getCount()

        + " times");

    counter.increment();

   }

  }

  需要注意这个例子中的两个非常关键的点:

  · MyCounter以及MyThreadLocal必须放到web应用的路径中,保被webapp classloader加载。

  · ThreadLocal类一定得是ThreadLocal的继承类,比如例子中的MyThreadLocal,因为ThreadLocal本来被common classloader加载,其生命周期与tomcat容器一致。ThreadLocal的继承类包括比较常见的NamedThreadLocal,注意不要踩坑。

  假如LeakingServlet所在的web应用启动,MyThreadLocal类也会被webapp classloader加载,如果此时web应用下线,而线程的生命周期未结束(比如为LeakingServlet提供服务的线程是一个线程池中的线程),那会导致myThreadLocal的实例仍然被这个线程引用,而不能被GC,期初看来这个带来的问题也不大,因为myThreadLocal所引用的对象占用的内存空间不太多,问题在于myThreadLocal间接持有加载web应用的webapp classloader的引用(通过myThreadLocal.getClass().getClassLoader()可以引用到),而加载web应用的webapp classloader有持有它加载的所有类的引用,这就引起了classloader泄露,它泄露的内存就非常可观了。

领取试听课
温馨提示:为不影响您的学业,来校区前请先电话或QQ咨询,方便我校安排相关的专业老师为您解答
版权所有:搜学搜课(www.soxsok.com) 技术支持:搜学搜课网