java - java类加载器内存泄漏示例
问题描述
我发现了一个非常有趣的内存泄漏示例。
public class Misc {
public static final String sql;
public static String dbData;
static {
System.out.println("static started");
sql = "select";
dbData = GetDBData.execute();
System.out.println("static ended");
}
public static String getDbData() {
System.out.println("getDbData");
return dbData;
}
}
public class GetDBData {
private String sql = Constants.mysql;
public String getMessage(){
System.out.println("getMessage");
if ("select".equalsIgnoreCase(sql))
return "dbData";
return "nodata";
}
public static String execute(){
System.out.println("execute");
GetDBData myInst = new GetDBData();
return myInst.getMessage();
}
}
public class Constants {
public static final String mysql = Misc.sql;
}
public class Main {
static Thread t = new Thread() {
@Override
public void run() {
Thread.currentThread().setName("GETDATA THREAD1");
System.out.println("--------" + Thread.currentThread().getName() + " started");
System.out.println("--------" + Thread.currentThread().getName() + "-" + Misc.getDbData());
}
};
static Thread t2 = new Thread() {
@Override
public void run() {
Thread.currentThread().setName("Constants THREAD");
System.out.println("--------" + Thread.currentThread().getName() + " started");
System.out.println("--------" + Thread.currentThread().getName() + "-" + Constants.mysql);
}
};
public static void main(String[] args) {
t.start();
t2.start();
}
}
如果您多次运行代码,则代码会阻塞在 Misc 类的静态块上,但有时会通过并执行完成。
在我基于 struts 的应用程序代码中,我有一个 jsp 文件,它正在调用诸如 <% Misc.getDbData() %> 之类的 Misc 类,而在 Misc 而不是 sql 中,我有 MessageResources sqlResources。部署后有时 5 分钟无法正常工作。(我收到类似 java.sql.SQLException 的错误:字符、令牌或子句无效或丢失)
我在想,这可能是上面类加载器的问题,但 tomcat 最终设法解决了这个问题。
可能的解决方案:
- 在一个巨人(我这里有多种配置)单例中转换 Misc 类,我必须在 10000 多个地方进行重构。
- 将类常量从杂项中解耦,但我还有另一个基于系统版本的属性。
- 将 Misc.getDbData() 从 jsp 转移到行动中会是一个临时解决方案吗?jsp/action 的线程是如何工作的?
- 您有其他解决方案吗?
解决方案
首先,这与内存泄漏无关。
至于解决方案,您可能应该只对 进行延迟初始化dbData
,因此可以在创建其中一个实例之前初始化所有类。
public class Misc {
public static final String sql;
public static String dbData;
static {
System.out.println("static started");
sql = "select";
System.out.println("static ended");
}
public static synchronized String getDbData() {
System.out.println("getDbData");
if (dbData == null) {
dbData = GetDBData.execute();
}
return dbData;
}
}
更新:问题代码问题的解释。
当第一次需要一个类时,它将被加载到内存中,半格式的类被锁定,并且静态初始化程序开始执行。在此期间,无法从另一个线程访问该类。
问题代码启动2个线程:
- 在线程中,
t
类按顺序加载Misc
,,,。GetDBData
Constants
- 在线程
t2
中,加载顺序是Constants
,Misc
,GetDBData
.
现在是一个竞争条件:如果一个线程在另一个线程到达那里之前完成了所有 3 个线程的初始化,那么就没有问题。
如果它们并行运行:
t
将加载/锁定Misc
并将t2
加载/锁定Constants
t
将加载/锁定GetDBData
并t2
等待Misc
t
将等待Constants
线程现在已死锁。
推荐阅读
- json - 在 Flutter 中解析 JSON 文件时出现空错误
- python - Python groupby 根据一列聚合多列
- google-tag-manager - 用于服务器端标记的 GTM 中的 Facebook 像素等效项是什么?
- cadence-workflow - 使用命令行运行工作流时,如何使工作流运行无限长时间?
- python - 更改函数列表时出现意外结果 (lambda)
- java - 在我使用 Firebase 的 Android 项目中,我收到此警告:W / System: Ignoring header X-Firebase-Locale because its value is null
- flutter - 如何正确模拟 Flutter Fire Firebase 函数?
- php - 如何使用日光浴室按日期过滤
- javascript - jQuery:删除动态添加的项目后重新索引顺序
- flutter - 应用程序空闲较长时间后通知丢失 - 一个信号 sdk 在颤动