java class 加载程序内存泄漏示例
java class loader memory leak example
我发现了一个非常有趣的内存泄漏示例。
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 class 的静态块上,但有时会通过并执行完成。
在我基于 struts 的应用程序代码中,我有一个 jsp 文件正在调用 Misc class 类似这样的东西 <% Misc.getDbData() %> 并且在 Misc而不是 sql 我有 MessageResources sqlResources。
部署后有时 5 分钟无法正常工作。(我收到这样的错误 java.sql.SQLException:字符、标记或子句无效或丢失)
我在想,这可能是上面 class loader 的问题,但 tomcat 最终设法解决了这个问题。
可能的解决方案:
- transform Misc class 一个巨大的(我这里有多个配置)单例,我要重构10000多个地方
- 将class常量与Misc解耦,但我还有另一个属性基于系统版本。
- 将 Misc.getDbData() 从 jsp 移动到行动中将是一个临时解决方案吗? jsp/action 的线程如何工作?
- 您还有其他解决方案吗?
首先,这与内存泄漏无关。
至于解决方案,您可能应该只对 dbData
进行延迟初始化,因此可以在创建其中一个实例之前初始化所有 classes。
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;
}
}
更新:问题代码的解释。
当第一次需要一个class时,它会被加载到内存中,半形成的class被锁定,静态初始化器开始执行。在此期间,无法从另一个线程访问 class。
问题代码启动2个线程:
- 在线程
t
中,class 正在按顺序加载 Misc
、GetDBData
、Constants
。
- 在线程
t2
中,加载顺序为Constants
、Misc
、GetDBData
.
现在是竞争条件:如果其中一个线程在另一个线程到达之前完成所有 3 个初始化,则没有问题。
如果他们 运行 并行:
t
将 load/lock Misc
并且 t2
将 load/lock Constants
t
将 load/lock GetDBData
并且 t2
将等待 Misc
t
将等待 Constants
线程现在 死锁。
我发现了一个非常有趣的内存泄漏示例。
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 class 的静态块上,但有时会通过并执行完成。
在我基于 struts 的应用程序代码中,我有一个 jsp 文件正在调用 Misc class 类似这样的东西 <% Misc.getDbData() %> 并且在 Misc而不是 sql 我有 MessageResources sqlResources。 部署后有时 5 分钟无法正常工作。(我收到这样的错误 java.sql.SQLException:字符、标记或子句无效或丢失)
我在想,这可能是上面 class loader 的问题,但 tomcat 最终设法解决了这个问题。
可能的解决方案:
- transform Misc class 一个巨大的(我这里有多个配置)单例,我要重构10000多个地方
- 将class常量与Misc解耦,但我还有另一个属性基于系统版本。
- 将 Misc.getDbData() 从 jsp 移动到行动中将是一个临时解决方案吗? jsp/action 的线程如何工作?
- 您还有其他解决方案吗?
首先,这与内存泄漏无关。
至于解决方案,您可能应该只对 dbData
进行延迟初始化,因此可以在创建其中一个实例之前初始化所有 classes。
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;
}
}
更新:问题代码的解释。
当第一次需要一个class时,它会被加载到内存中,半形成的class被锁定,静态初始化器开始执行。在此期间,无法从另一个线程访问 class。
问题代码启动2个线程:
- 在线程
t
中,class 正在按顺序加载Misc
、GetDBData
、Constants
。 - 在线程
t2
中,加载顺序为Constants
、Misc
、GetDBData
.
现在是竞争条件:如果其中一个线程在另一个线程到达之前完成所有 3 个初始化,则没有问题。
如果他们 运行 并行:
t
将 load/lockMisc
并且t2
将 load/lockConstants
t
将 load/lockGetDBData
并且t2
将等待Misc
t
将等待Constants
线程现在 死锁。