java: web应用中不经意的内存泄露

news/2024/11/6 15:25:21/

前面有一篇讲解如何在spring mvc web应用中一启动就执行某些逻辑,今天无意发现如果使用不当,很容易引起内存泄露,测试代码如下:

1、定义一个类App

package com.cnblogs.yjmyzz.web.controller;import java.util.Date;public class App {boolean isRun = false;public App() {isRun = true;}public void start() {while (isRun) {System.out.println("=======> I AM ALIVE =>" + new Date());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}public void stop() {isRun = false;}}

代码里面的内容不是重点,只是示意一下,我打算在spring mvc 应用一启动时,就让这个类实例化,执行其中的start方法,即:每隔一秒输出一句话。

2、定义一个Listener

import com.cnblogs.yjmyzz.web.controller.App;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;@Component
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {App app;@Overridepublic void onApplicationEvent(ContextRefreshedEvent evt) {if (evt.getApplicationContext().getParent() == null) {new Thread(new Runnable() {@Overridepublic void run() {app = new App();app.start();}}).start();}}
}

代码也很简单,应用一启动,就开一个线程,实例化App,然后调用app.start()方法,运行一下,也跟预期的一样,每隔一秒输出类似下面的内容:

=======> I AM ALIVE =>Wed Sep 16 21:55:42 CST 2015

正式部署到jboss上以后,问题来了,在jboss管理控制台上,把这个应用给disable甚至remove后,日志里仍然不断有上面的类似输出,即app的实例仍然活着,其start方法也始终在运行,换句话说,app并没有被销毁。

简单分析一下:jboss的每个server启动后,会伴随启动一个jvm实例,而部署在该server上的web应用,里面创建的各种资源也在这个jvm实例中,就算把应用给停掉甚至删除,由于代码中没有任何清除app或

另一个问题:如果把上面这段代码中,创建线程的部分去掉,改成直接 app = new App(); app.start(); 部署时会发现另一个现象,日志里仍然不断有输出,即代码在执行,但是该应用在jboss中的状态始终是isdeploying,部署一直无法结束,始终处于『部署中』的状态。

原因:start方法中的Thread.sleep()方法会阻塞线程,导致部署无法执行完毕。

解决办法:

停止start方法的处理,所以这个实例一直存在,不会被销毁,除非server重启。

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Date;@Component
public class App {boolean isRun = false;@PostConstructpublic void init() {System.out.println("init ==> " + new Date());isRun = true;}public void start() {while (isRun) {System.out.println("=======> I AM ALIVE =>" + new Date());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}public void stop() {isRun = false;}@PreDestroypublic void destroy() {System.out.println("destroy ==> " + new Date());stop();}
}

这里做了几处改进:

a) 加上@Component后,App的实例将由Spring容器自动创建,即由容器来管理

b) 加上了@PreDestroy,Bean的生命周期由Spring容器来管理后,凡是Bean里加上该注解的方法,会在Bean销毁前被执行,通常该方法用于清理资源

c) 将初始化的工作,移到了init方法中,并通过@PostConstruct注解告诉Spring,在调用完Bean的默认构造方法后,自动来调用该方法(当然这一步是可选的,并非必须)

@Component
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {@AutowiredApp app;@Overridepublic void onApplicationEvent(ContextRefreshedEvent evt) {if (evt.getApplicationContext().getParent() == null) {new Thread(new Runnable() {@Overridepublic void run() {app.start();}}).start();}}

Listener中就简单多了,直接@Autowired注入app实例就行了。

个人建议:

a) 如果要在web 应用一启动时,就执行某些操作,特别是对资源类的长连接实例创建(比如:加载数据到缓存中预热、连接到Zookeeper监控节点变化、连接到Ftp准备取数据),最好交给Spring容器来自动创建,且务必记得在Destroy前,清理资源(即:断开连接)

b) 在启动的执行逻辑中,不要使用阻塞线程的操作(比如:Thread.sleep之类的方法),否则部署时,实际上代码已经在后台执行了,jboss管理控制台上,一直处于部署中的状态,也没有任何输出,让人一头雾水,折腾半天才能定位错误,很浪费时间,如果是线上生产环境,是要粗事情的。


http://www.ppmy.cn/news/50184.html

相关文章

【数据挖掘】5分钟带你了解文本向量化的常见方式

5分钟带你了解文本向量化的常见方式 1. 独特编码模型2. 词袋模型3. TF-IDF模型4. N-gram模型5. Word2Vec模型参考资料文本向量化:将文本信息表示成能够表达文本语义的向量,是 用数值向量来表示文本的语义。 词嵌入(Word Embedding):一种将文本中的词转换成数字向量的方法,…

Elasticsearch REST API 文档管理

文章目录 创建文档路径参数常用查询参数示例响应说明 查询文档路径参数编辑查询参数示例 1响应说明示例2示例3 更新文档路径参数查询参数示例1示例2禁用noop mget 获取多个文档路径参数查询参数请求正文参数说明示例1响应结果示例2 删除文档路径参数查询参数示例1 开放式并发控…

Redis7

Redis之父安特雷兹 Redis7概述 Redis:Remote Dictionary Server(远程字典服务)是完全开源的&#xff0c;使用ANSIC语言编写遵守BSD协议&#xff0c;是一个高性能的Key-Value数据库提供了丰富的数据结构&#xff0c;例如String、Hash、List、Set、SortedSet等等。数据是存在内…

docker离线部署 升级

其它版本linux内核系统或许略有不同 下载docker版本包 https://download.docker.com/linux/static/stable/x86_64/centos部署版本 上传到服务器目录下&#xff0c;解压文件。 tar -xvf docker-XXXXXX.tgz将解压出来的docker文件内容移动到 /usr/bin/ 目录下&#xff0c;该命…

FE_CSS 常见布局技巧

1 巧妙运用浮动元素不会压住文字的特性 float: left; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta ht…

java中的锁

java中有哪些锁 这个问题在我看了一遍<java并发编程>后尽然无法回答,说明自己对于锁的概念了解的不够。于是再次翻看了一下书里的内容,突然有点打开脑门的感觉。看来确实是要学习的最好方式是要带着问题去学,并且解决问题。 在java中锁主要两类:内部锁synchronized…

网络通信基础 - 多路复用技术(频分多路复用、时分多路复用、波分多路复用)

文章目录 1 概述1.1 复用器 MUX 2 分类2.1 频分多路复用 FDM2.2 时分多路复用 TDM2.3 波分多路复用 WDM 1 概述 1.1 复用器 MUX 多路复用技术&#xff1a;把多个低速信道组合成一个高速信道的技术这种技术要用到两个设备&#xff0c;统称为 多路器&#xff08;MUX&#xff09…

持续集成——接口测试集成实战

文章目录 一、接口测试持续集成的好处二、环境准备三、Jenkins节点挂载1、新建node节点2、编辑节点信息 四、节点环境的配置1、Python3环境2、allure-commandline工具3、allure插件 五、本地运行待测代码1、Pycharm拉取代码执行2、命令行运行代码&#xff0c;并生成报告 六、库…