怎么样写出带Bug的代码
1、创建class时不没有重写hashCode()和equals()方法,轻则创建的对象比较时无法区分,重则将大量对象存储至map时导致内存泄漏。
解决方法:根据业务需要重写equals()方法和hashCode()方法。
2、内部类引用外部类
2.1、非静态的内部类默认会持有外部类,尽管代码上不再使用外部类,所以如果有地方引用了这个非静态内部类,会导致外部类也被引用,垃圾回收时无法回收这个外部类。
举例
public class Outer{ private byte[] bytes = new byte[1024]; //外部类持有数据 private String name = "Bug"; class Inner{ private String name; public Inner() { this.name = Outer.this.name; } } public static void main(String[] args) throws IOException, InterruptedException { int count = 0; ArrayList<Inner> inners = new ArrayList<>(); while (true){ if(count++ % 100 == 0){ Thread.sleep(10); } inners.add(new Outer().new Inner()); } } }
解决方法:使用静态内部类
public class FixOuter { private byte[] bytes = new byte[1024 * 1024]; //外部类持有数据 private static String name = "Bug"; static class Inner{ private String name; public Inner() { this.name = FixOuter.name; } } public static void main(String[] args) throws IOException, InterruptedException { int count = 0; ArrayList<Inner> inners = new ArrayList<>(); while (true){ if(count++ % 100 == 0){ Thread.sleep(10); } inners.add(new Inner()); } } }
2.2、匿名内部类对象如果在非静态方法中被创建,会持有调用者对象,垃圾回收时无法回收调用者。
举例
public class Outer { private byte[] bytes = new byte[1024]; public List<String> newList() { List<String> list = new ArrayList<String>() {{ add("1"); add("2"); }}; return list; } public static void main(String[] args) throws IOException { int count = 0; ArrayList<Object> objects = new ArrayList<>(); while (true){ System.out.println(++count); objects.add(new Outer().newList()); } } }
解决方法:使用静态方法
public class FixOuter { private byte[] bytes = new byte[1024]; public static List<String> newList() { List<String> list = new ArrayList<String>() {{ add("1"); add("2"); }}; return list; } public static void main(String[] args) throws IOException { int count = 0; ArrayList<Object> objects = new ArrayList<>(); while (true){ System.out.println(++count); objects.add(newList()); } } }
3、ThreadLocal使用不当
3.1、ThreadLocal变量会与线程绑定,如果线程长时间存在(例如线程池中的线程),而ThreadLocal变量没有被及时移除,可能会导致内存泄漏。
解决方法在使用完ThreadLocal变量后,调用remove()方法移除变量。
3.2、ThreadLocal本身是线程安全的,但需要注意在使用ThreadLocal变量时,不要在多个线程中共享同一个ThreadLocal实例。
解决方法每个线程使用自己的ThreadLocal实例,或者使用InheritableThreadLocal来实现线程间的数据传递
4、IO或者网络资源没有正确关闭
解决方法:在finally块中关闭不再使用的资源;从 Java 7 开始,使用try-with-resources语法可以用于自动关闭资源
5、Arrays.asList()方法使用不当
List<String> list = Arrays.asList("a", "b", "14a", "36B");
String remove = list.remove(0);
java.lang.UnsupportedOperationException
at java.base/java.util.AbstractList.remove(AbstractList.java:167)
解决方法:使用错误源于对Arrays.asList()说明不够了解,想快速简单可编辑List可以在使用Arrays.asList()创建后再使用ArrayList进行一下再包装
List<String> list = Arrays.asList("a", "b", "14a", "36B");
ArrayList<String> l = new ArrayList<>(list);
String remove = l.remove(0);
6、CompletableFuture线程池配置不当,默认线程池使用的是ForkJoinPool,ForkJoinPool的cpu默认核心数是当前cup核心数(jdk17)在IO密集型任务中,这会导致大量任务排队等待,可能会导致内存溢出(任务提交速度 大于 任务处理速度)
public static void main(String[] args) {
// 统计已提交的任务数量(线程安全)运行前建议设置 JVM 参数:-Xmx64m (或更小)以快速触发 OOM
AtomicLong submittedCount = new AtomicLong(0);
try {
// 无限循环提交任务
while (true) {
// 每个任务携带一个 1KB 的数组,增加内存占用,加速 OOM
byte[] data = new byte[1024]; // 1 KB
// 提交异步任务(默认使用 ForkJoinPool.commonPool())
CompletableFuture.runAsync(() -> {
// 通过引用 data 确保数组被 lambda 捕获,使其在任务执行前不会被 GC
int len = data.length; // 仅用于确保捕获
// 模拟 IO 密集型操作(例如网络请求、文件读写等)
try {
TimeUnit.SECONDS.sleep(2); // 2 秒,处理速度极慢 IO 操作特点:线程大部分时间在等待,不占用 CPU
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 更新提交计数并每 10,000 次打印一次进度
long count = submittedCount.incrementAndGet();
if (count % 10000 == 0) {
System.out.println("已提交任务数: " + count);
}
}
} catch (Throwable t) {
// 捕获任何错误(包括 OutOfMemoryError),打印信息后退出
System.err.println("捕获到异常/错误: " + t);
t.printStackTrace();
System.err.println("崩溃前总计提交任务数: " + submittedCount.get());
// 短暂等待,让部分已完成的任务有机会输出日志(如果有)
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException ie) {
// ignore
}
System.exit(1);
}
}
解决方法:根据业务场景需求使用测试数据多轮压测确定线程池配置
public static void main(String[] args) {
// 运行前建议设置 JVM 参数:-Xmx256m (无需过小,观察内存平稳即可)
// 获取 CPU 核心数,用于估算线程池大小
int processors = Runtime.getRuntime().availableProcessors();
// IO 密集型任务可适当调大线程数(例如 2~4 倍核心数)
int corePoolSize = processors * 2; // 核心线程数
int maximumPoolSize = processors * 4; // 最大线程数
long keepAliveTime = 60L; // 空闲线程存活时间
TimeUnit unit = TimeUnit.SECONDS;
// 使用有界队列,容量固定,避免无限积压
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(1000);
// 拒绝策略:调用者运行(让提交任务的线程自己执行任务)
// 这样当队列满时,主线程会参与任务执行,从而减缓提交速度,形成背压
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
// 创建自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
handler
);
// 允许核心线程超时回收,避免空闲线程长期占用资源
executor.allowCoreThreadTimeOut(true);
// 计数器:记录已提交任务数(线程安全)
AtomicLong submittedCount = new AtomicLong(0);
// 注册 JVM 关闭钩子,确保程序退出时优雅关闭线程池
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("\n正在关闭线程池...");
executor.shutdown();
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}));
try {
// 无限循环提交任务,模拟持续的高负载
while (true) {
// 每个任务携带一个 1KB 的数组,模拟内存占用(实际业务中可能是 IO 缓冲区)
byte[] data = new byte[1024];
// 提交异步任务,使用自定义线程池
CompletableFuture.runAsync(() -> {
// 通过引用 data 确保数组在任务执行前不被 GC
int len = data.length; // 仅用于捕获 data
// 模拟 IO 密集型操作(例如数据库查询、远程调用等)
try {
TimeUnit.SECONDS.sleep(2); // 2 秒,远慢于提交速度
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, executor);
// 更新计数器,并定期打印线程池状态
long count = submittedCount.incrementAndGet();
if (count % 1000 == 0) {
System.out.printf("已提交任务数: %d, 队列大小: %d, 活跃线程数: %d, 已完成任务数: %d%n",
count,
executor.getQueue().size(),
executor.getActiveCount(),
executor.getCompletedTaskCount());
}
}
} catch (Throwable t) {
// 捕获任何异常或错误(包括 OOM,但本示例中应不会发生)
System.err.println("捕获到异常/错误: " + t);
t.printStackTrace();
executor.shutdownNow();
}
}
7、CompletableFuture异常丢失,这是CompletableFuture的异常处理机制的特性
public static void main(String[] args){
System.out.println("异常被彻底吞掉!");
CompletableFuture.supplyAsync(() -> {
System.out.println("→ 任务开始执行...");
if (true) throw new RuntimeException("数据库连接超时!"); // 模拟故障
return "成功数据";
})
.thenApply(result -> {
// 陷阱1:前序已异常,此代码块根本不会执行!
// 陷阱2:此处的try-catch毫无意义(根本进不来)
try { return result.toUpperCase(); }
catch (Exception e) { return "兜底"; }
})
.thenAccept(System.out::println); // 陷阱3:无异常处理,无get/join
try {
// 等待异步线程(但异常已丢失!)
TimeUnit.SECONDS.sleep(2);
} catch (Exception e){
e.printStackTrace();
}
System.out.println("主线程结束,异未出现");public static void main(String[] args){
System.out.println("异常被彻底吞掉!");
CompletableFuture.supplyAsync(() -> {
System.out.println("→ 任务开始执行...");
if (true) throw new RuntimeException("数据库连接超时!"); // 模拟故障
return "成功数据";
})
.thenApply(result -> {
// 陷阱1:前序已异常,此代码块根本不会执行!
// 陷阱2:此处的try-catch毫无意义(根本进不来)
try { return result.toUpperCase(); }
catch (Exception e) { return "兜底"; }
})
.thenAccept(System.out::println); // 陷阱3:无异常处理,无get/join
try {
// 等待异步线程(但异常已丢失!)
TimeUnit.SECONDS.sleep(2);
} catch (Exception e){
e.printStackTrace();
}
System.out.println("主线程结束,异未出现");
解决方法:在CompletableFuture调用链中使用whenComplete方法,末尾使用handle方法和exceptionally方,调用get或者 join方法
public static void main(String[] args) {
System.out.println("异常精准捕获+降级+监控");
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> {
if (Math.random() > 0.5)
// throw new IllegalStateException("API限流");
throw new RuntimeException("数据库连接超时!");
return "原始数据";
}, Executors.newSingleThreadExecutor()) // 显式指定线程工厂(JDK17推荐)
.whenComplete((result, throwable) -> {
if (Objects.nonNull(throwable)) {
System.err.println(throwable);
} else {
System.out.println(result);
}
})
// 第一层:链内精准处理(推荐handle)
.handle((result, ex) -> {
if (ex != null) {
System.err.println("捕获异常: " + ex.getMessage());
// 记录监控指标(示例)
// Metrics.counter("cf_error").increment();
return "【降级数据】"; // 返回安全默认值
}
return result.toUpperCase();
})
// 第二层:链尾兜底(防止handle遗漏)
.exceptionally(ex -> {
System.err.println("链尾兜底异常: " + ex.getMessage());
return "【终极降级】";
});
// 第三层:强制获取结果(生产环境必须!)
try {
String result = future.get(3, TimeUnit.SECONDS); // 显式超时控制
System.out.println("最终结果: " + result);
} catch (TimeoutException e) {
System.err.println("任务超时!");
future.cancel(true); // 取消任务
} catch (ExecutionException e) {
// JDK 17中:getCause()可获取原始异常(非CompletionException包装)
System.err.println("执行异常: " + e.getCause().getMessage());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("线程中断");
}
}

浙公网安备 33010602011771号