问题描述:
用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的内存可以忽略。