问题描述:
用java swing写了个记事本程序,并打成了exe文件在windows下运行,但是每次启动,都会启动一个java虚拟机,每个java虚拟机的启动,都会大量消耗内存。特别是将系统txt文件的默认打开方式设置为本记事本程序时,每双击打开一个记事本,就启动一个jvm,内存消耗30于M,更重要的是,启动jvm很耗时间,在自己的机器上(i5处理器)启动该记事本程序需要2—5秒的时间,用着很不爽。
解决办法:
第一次启动程序时,同时启动一个监听,当以后再次启动记事本程序时,首先与监听器进行通信,并将用户操作信息发送给监听器,用户的操作由监听器所属的java虚拟机完成,如此则保证了所有记事本的相关操作在同一个jvm中进行。
代码:
- public static void main(String args[]) {
- final String[] filePathArray = args;
- //设定端口、IP
- final String ip = "127.0.0.1";
- final int port = 12530;
- ObjectOutputStream out = null;
- Socket socket = null;
- try {
- //检测记事本程序是否已启动,如果已启动,则由已启动的记事本程序来新建或打开文档,如果未启动,则启动记事本程序,并启动监听。
- //连接本机指定端口,如果连接失败(表现形式,抛Connect异常),则说明记事本程序尚未启动。
- socket = new Socket(ip,port);
- out = new ObjectOutputStream(socket.getOutputStream());
- //根据路径打开指定文档
- if(filePathArray != null && filePathArray.length > 0){
- out.writeObject(filePathArray);
- //新建空白文档
- }else{
- out.writeObject("new");
- }
- SplashScreen ss = SplashScreen.getSplashScreen();
- if (ss != null) {
- ss.close();
- }
- //退出java虚拟机
- System.exit(0);
- } catch (Exception e) {
- //-----------抛异常说明记事本程序尚未启动-------------
- //1、================初始化日志=================
- log = Logger.getLogger(Index.class);
- // 2、================验证应用程序同级目录下,log文件夹是否存在,如果不存在,则创建。================
- File file_log = new File("log");
- if (!file_log.exists() || !file_log.isDirectory()) {
- if (!file_log.mkdirs()) {
- JOptionPane.showMessageDialog(null, "创建log目录失败!");
- System.exit(0);
- }
- }
- // 3、================验证应用程序同级目录下,config文件夹是否存在,如果不存在,则创建。================
- File file_config = new File("config");
- if (!file_config.exists() || !file_config.isDirectory()) {
- if (!file_config.mkdirs()) {
- JOptionPane.showMessageDialog(null, "创建config目录失败!");
- System.exit(0);
- }
- }
- //4、================新启线程,监听socket连接================
- new Thread(){
- @Override
- public void run() {
- ObjectInputStream serverIn = null;
- Socket clientSocket = null;
- try {
- ServerSocket serverSocket = new ServerSocket(port);
- while(true){
- //accept()会阻塞当前线程的继续执行,当有新客户端接入,才会向下运行。
- clientSocket = serverSocket.accept();
- serverIn = new ObjectInputStream(clientSocket.getInputStream());
- //取得客户端发送的数据
- final Object getMsg = serverIn.readObject();
- //每个记事本启动时,只会向服务器端发一条信息(new 或 要打开文档的地址),所以处理完请求则关闭相应资源。
- try {
- serverIn.close();
- } catch (IOException e) {
- log.error(null, e);
- }
- try {
- clientSocket.close();
- } catch (Exception e) {
- log.error(null, e);
- }
- //如果客户端发送的信息是 new ,则新建空白文档
- if(getMsg.toString().equals("new")){
- SwingUtilities.invokeLater(new Runnable(){
- @Override
- public void run() {
- MyNotePad note = new MyNotePad(null, null, null);
- note.setVisible(true);
- }
- });
- }else{
- //否则,根据客户端发送过来的路径,打开指定文档。
- SwingUtilities.invokeLater(new Runnable(){
- @Override
- public void run() {
- String[] files = (String[])getMsg;
- for(int i=0;i<files.length;i++){
- MyNotePad note = new MyNotePad(null, null,files[i]);
- note.setVisible(true);
- }
- }
- });
- }
- }
- } catch (Exception e2) {
- log.error(null, e2);
- }
- }
- }.start();
- }
- //5、================启动记事本程序================
- // 设置系统字体
- SysFontAndFace.setSysFontAndFace();
- // 设置输入中文时,不显示输入框
- System.setProperty("java.awt.im.style", "on-the-spot");
- try {
- //打开指定路径的文档
- if (filePathArray != null && filePathArray.length > 0) {
- SwingUtilities.invokeAndWait(new Runnable(){
- @Override
- public void run() {
- for (int i = 0; i < filePathArray.length; i++) {
- MyNotePad note = new MyNotePad(null, null,filePathArray[i]);
- note.setVisible(true);
- }
- }
- });
- // 打开空白文档
- } else {
- SwingUtilities.invokeAndWait(new Runnable(){
- @Override
- public void run() {
- MyNotePad note = new MyNotePad(null, null, null);
- note.setVisible(true);
- }
- });
- }
- } catch (Exception e) {
- JOptionPane.showMessageDialog(null, "出错了,请重新启动!");
- System.exit(1);
- }
- //6、================启动定时任务,负责执行gc================
- //创建定时器,一分钟后开始执行,以后每分钟执行一次。
- Timer timer = new Timer();
- timer.schedule(new TimerTask(){
- @Override
- public void run() {
- System.gc() ;
- }
- }, 60*1000,60*1000);
- // 7、================关闭欢迎信息================
- SplashScreen ss = SplashScreen.getSplashScreen();
- if (ss != null) {
- ss.close();
- }
- }
public static void main(String args[]) {
final String[] filePathArray = args;
//设定端口、IP
final String ip = "127.0.0.1";
final int port = 12530;
ObjectOutputStream out = null;
Socket socket = null;
try {
//检测记事本程序是否已启动,如果已启动,则由已启动的记事本程序来新建或打开文档,如果未启动,则启动记事本程序,并启动监听。
//连接本机指定端口,如果连接失败(表现形式,抛Connect异常),则说明记事本程序尚未启动。
socket = new Socket(ip,port);
out = new ObjectOutputStream(socket.getOutputStream());
//根据路径打开指定文档
if(filePathArray != null && filePathArray.length > 0){
out.writeObject(filePathArray);
//新建空白文档
}else{
out.writeObject("new");
}
SplashScreen ss = SplashScreen.getSplashScreen();
if (ss != null) {
ss.close();
}
//退出java虚拟机
System.exit(0);
} catch (Exception e) {
//-----------抛异常说明记事本程序尚未启动-------------
//1、================初始化日志=================
log = Logger.getLogger(Index.class);
// 2、================验证应用程序同级目录下,log文件夹是否存在,如果不存在,则创建。================
File file_log = new File("log");
if (!file_log.exists() || !file_log.isDirectory()) {
if (!file_log.mkdirs()) {
JOptionPane.showMessageDialog(null, "创建log目录失败!");
System.exit(0);
}
}
// 3、================验证应用程序同级目录下,config文件夹是否存在,如果不存在,则创建。================
File file_config = new File("config");
if (!file_config.exists() || !file_config.isDirectory()) {
if (!file_config.mkdirs()) {
JOptionPane.showMessageDialog(null, "创建config目录失败!");
System.exit(0);
}
}
//4、================新启线程,监听socket连接================
new Thread(){
@Override
public void run() {
ObjectInputStream serverIn = null;
Socket clientSocket = null;
try {
ServerSocket serverSocket = new ServerSocket(port);
while(true){
//accept()会阻塞当前线程的继续执行,当有新客户端接入,才会向下运行。
clientSocket = serverSocket.accept();
serverIn = new ObjectInputStream(clientSocket.getInputStream());
//取得客户端发送的数据
final Object getMsg = serverIn.readObject();
//每个记事本启动时,只会向服务器端发一条信息(new 或 要打开文档的地址),所以处理完请求则关闭相应资源。
try {
serverIn.close();
} catch (IOException e) {
log.error(null, e);
}
try {
clientSocket.close();
} catch (Exception e) {
log.error(null, e);
}
//如果客户端发送的信息是 new ,则新建空白文档
if(getMsg.toString().equals("new")){
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run() {
MyNotePad note = new MyNotePad(null, null, null);
note.setVisible(true);
}
});
}else{
//否则,根据客户端发送过来的路径,打开指定文档。
SwingUtilities.invokeLater(new Runnable(){
@Override
public void run() {
String[] files = (String[])getMsg;
for(int i=0;i<files.length;i++){
MyNotePad note = new MyNotePad(null, null,files[i]);
note.setVisible(true);
}
}
});
}
}
} catch (Exception e2) {
log.error(null, e2);
}
}
}.start();
}
//5、================启动记事本程序================
// 设置系统字体
SysFontAndFace.setSysFontAndFace();
// 设置输入中文时,不显示输入框
System.setProperty("java.awt.im.style", "on-the-spot");
try {
//打开指定路径的文档
if (filePathArray != null && filePathArray.length > 0) {
SwingUtilities.invokeAndWait(new Runnable(){
@Override
public void run() {
for (int i = 0; i < filePathArray.length; i++) {
MyNotePad note = new MyNotePad(null, null,filePathArray[i]);
note.setVisible(true);
}
}
});
// 打开空白文档
} else {
SwingUtilities.invokeAndWait(new Runnable(){
@Override
public void run() {
MyNotePad note = new MyNotePad(null, null, null);
note.setVisible(true);
}
});
}
} catch (Exception e) {
JOptionPane.showMessageDialog(null, "出错了,请重新启动!");
System.exit(1);
}
//6、================启动定时任务,负责执行gc================
//创建定时器,一分钟后开始执行,以后每分钟执行一次。
Timer timer = new Timer();
timer.schedule(new TimerTask(){
@Override
public void run() {
System.gc() ;
}
}, 60*1000,60*1000);
// 7、================关闭欢迎信息================
SplashScreen ss = SplashScreen.getSplashScreen();
if (ss != null) {
ss.close();
}
}
效果:
经前后对比试验,数据如下:
双击启动时间:改造前(2--5秒),改造后(几乎无延迟,1秒内)
同时打开多个文档:改造前(打开一次30M,打开几次则几个30M),改造后(打开一次与打开多次差距不大,始终维持在40M左右)
不足:
监听程序会常驻内存,关闭记事本程序不会释放(这也正是启动迅速的原因),不过现在的机器配置都很高,30M的内存可以忽略。