【java】ThreadLocal 内存泄漏 代码演示 实例演示

x33g5p2x  于2022-02-15 转载在 Java  
字(3.3k)|赞(0)|评价(0)|浏览(492)

1.概述

转载:ThreadLocal 内存泄漏 代码演示 实例演示

首先看文章:ThreadLocal内存泄露原因分析

相关文章:

【高并发】ThreadLocal、InheritableThreadLocal

【Java】Java中ThreadLocal简介以及源码

2.案例

2.1 不使用ThreadLocal

下面这段程序创建了一个有5个线程的线程池。
每个线程致性都申请5M大小的堆空间。

  1. public class MyThreadLocalOOM1 {
  2. public static final Integer SIZE = 500;
  3. static ThreadPoolExecutor executor = new ThreadPoolExecutor(
  4. 5, 5, 1,
  5. TimeUnit.MINUTES, new LinkedBlockingDeque<>());
  6. static class LocalVariable {//总共有5M
  7. private byte[] locla = new byte[1024 * 1024 * 5];
  8. }
  9. public static void main(String[] args) {
  10. try {
  11. for (int i = 0; i < SIZE; i++) {
  12. executor.execute(() -> {
  13. new LocalVariable();
  14. System.out.println("开始执行");
  15. });
  16. Thread.sleep(100);
  17. }
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }

使用JDK自带的VisualVM来观察对内存占用情况,下图中锯齿状的蓝色区域是堆已经使用的空间大小,可以看到在0-70内,这是因为每个线程都会申请5M空间,过一小段时间后,就会触发一次youngGC, 内存就会释放。

在19:30:36处我手动触发了一次GC ,可以看到堆空间基本都释放。

说明LocalVariable全都释放,未发生内存泄漏。

2.2 使用ThreadLocal,但不remove

  1. public class MyThreadLocalOOM2 {
  2. public static final Integer SIZE = 500;
  3. static ThreadPoolExecutor executor = new ThreadPoolExecutor(
  4. 5, 5, 1,
  5. TimeUnit.MINUTES, new LinkedBlockingDeque<>());
  6. static class LocalVariable {//总共有5M
  7. private byte[] locla = new byte[1024 * 1024 * 5];
  8. }
  9. static ThreadLocal<LocalVariable> local = new ThreadLocal<>();
  10. public static void main(String[] args) {
  11. try {
  12. for (int i = 0; i < SIZE; i++) {
  13. executor.execute(() -> {
  14. local.set(new LocalVariable());
  15. System.out.println("开始执行");
  16. });
  17. Thread.sleep(100);
  18. }
  19. local = null;//这里设置为null,依旧会造成内存泄漏
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }

上面代码中定义了static的ThreadLocal变量local, 但是当for循环致性完毕后,又将local设置为null。普通对象,此时就没有强引用了,当GC时就会被回收掉。

但是通过下面图可以看到,即使for循环结束后手动触发了GC,堆内存空间依旧占用约25MB空间,正好是线程池中5个线程的LocalVariable对象的空间和。

所以发生了内存泄漏。

发生内存泄漏的原因见 ThreadLocal内存泄露原因分析

2.3 使用Thread Local,且remove

  1. public class MyThreadLocalOOM3 {
  2. public static final Integer SIZE = 500;
  3. static ThreadPoolExecutor executor = new ThreadPoolExecutor(
  4. 5, 5, 1,
  5. TimeUnit.MINUTES, new LinkedBlockingDeque<>());
  6. static class LocalVariable {//总共有5M
  7. private byte[] locla = new byte[1024 * 1024 * 5];
  8. }
  9. final static ThreadLocal<LocalVariable> local = new ThreadLocal<>();
  10. public static void main(String[] args) {
  11. try {
  12. for (int i = 0; i < SIZE; i++) {
  13. executor.execute(() -> {
  14. local.set(new LocalVariable());
  15. System.out.println("开始执行");
  16. local.remove();
  17. });
  18. Thread.sleep(100);
  19. }
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }

上面代码中,线程致性完成后,都调用了local.remove()来将threadLocal内的对象删除。下图中可以看到在手动触发GC后,对内存全部释放,未发生内存泄漏。

2.4 单线程演示内存泄漏

  1. public class MyThreadLocalOOM4 {
  2. public static final Integer SIZE = 500;
  3. static class LocalVariable {//总共有50M
  4. private byte[] locla = new byte[1024 * 1024 * 50];
  5. }
  6. static ThreadLocal<LocalVariable> local = new ThreadLocal<>();
  7. static LocalVariable localVariable;
  8. public static void main(String[] args) throws InterruptedException {
  9. try {
  10. TimeUnit.SECONDS.sleep(2);
  11. localVariable = new LocalVariable();
  12. local.set(new LocalVariable());
  13. System.out.println("开始执行");
  14. Thread.sleep(100);
  15. local = null;
  16. localVariable = null;
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. while (true) {
  21. TimeUnit.SECONDS.sleep(1);
  22. }
  23. }
  24. }

从结果中可以看到,localVariable的50MB空间释放了,但是ThreadLocal中存放的50MB空间没有释放。

3.引用

像下面代码,stu是再for外面定义的,线程内每次都使用这个stu对象,那么依旧会有线程安全问题,因为该stu对象还是多线程之间共享这个对象。

所以一定要再每个线程内new对象,避免多线程共享。
也可以自己实现MyThreadLocal,来手动复制对象,避免复用同一个对象

p

  1. ublic class MyThreadLocal<T> extends ThreadLocal<T> {
  2. public void set(T value) {
  3. String s = JSONObject.toJSONString(value);
  4. super.set((T) JSONObject.parseObject(s, value.getClass()));
  5. }
  6. }

相关文章

最新文章

更多