前面有一篇讲解如何在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; @Override
public void onApplicationEvent(ContextRefreshedEvent evt) {
if (evt.getApplicationContext().getParent() == null) { new Thread(new Runnable() {
@Override
public 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或停止start方法的处理,所以这个实例一直存在,不会被销毁,除非server重启。

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

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

解决办法:

import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.Date; @Component
public class App { boolean isRun = false; @PostConstruct
public 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;
} @PreDestroy
public 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> { @Autowired
App app; @Override
public void onApplicationEvent(ContextRefreshedEvent evt) {
if (evt.getApplicationContext().getParent() == null) {
new Thread(new Runnable() {
@Override
public void run() {
app.start();
}
}).start();
} } }

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

个人建议:

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

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

java: web应用中不经意的内存泄露的更多相关文章

  1. Java宝典(四)------Java中也存在内存泄露。

    --Java中会存在内存泄露吗? --如果你想当然的以为Java里有了垃圾回收机制就不会存在内存泄露,那你就错了. Java里也会存在内存泄露! 我们慢慢来分析. 所谓内存泄露就是指一个不再被程序使用 ...

  2. 在Java Web程序中使用监听器可以通过以下两种方法

    之前学习了很多涉及servlet的内容,本小结我们说一下监听器,说起监听器,编过桌面程序和手机App的都不陌生,常见的套路都是拖一个控件,然后给它绑定一个监听器,即可以对该对象的事件进行监听以便发生响 ...

  3. JAVA WEB项目中各种路径的获取

    JAVA WEB项目中各种路径的获取 标签: java webpath文件路径 2014-02-14 15:04 1746人阅读 评论(0) 收藏 举报  分类: JAVA开发(41)  1.可以在s ...

  4. Java Web开发中MVC设计模式简介

    一.有关Java Web与MVC设计模式 学习过基本Java Web开发的人都已经了解了如何编写基本的Servlet,如何编写jsp及如何更新浏览器中显示的内容.但是我们之前自己编写的应用一般存在无条 ...

  5. iPhone应用中如何避免内存泄露?

    如何有效控制iPhone内存管理的对象的所有权与引用计数和以及iPhone内存的自动释放与便捷方法.本文将介绍在iPhone应用中如何避免内存泄露.想了解“在iPhone应用中如何避免内存泄露”就必须 ...

  6. 对开发中常见的内存泄露,GDI泄露进行检测

    对开发中常见的内存泄露,GDI泄露进行检测 一.GDI泄露检测方法: 在软件测试阶段,可以通过procexp.exe 工具,或是通过任务管理器中选择GDI对象来查看软件GDI的对象是使用情况. 注意点 ...

  7. linux 下用renameTo方法修改java web项目中文件夹名称问题

    经测试,在Linux环境中安装tomcat,然后启动其中的项目,在项目中使用java.io.File.renameTo(File dest)方法可行. 之前在本地运行代码可以修改,然后传到Linux服 ...

  8. 对Java Web项目中路径的理解

    第一个:文件分隔符 坑比Window.window分隔符 用\;unix采用/.于是用File.separator来跨平台 请注意:这是文件路径.在File f = new File(“c:\\hah ...

  9. 在Java Web项目中添加定时任务

    在Java Web程序中加入定时任务,这里介绍两种方式:1.使用监听器注入:2.使用Spring注解@Scheduled注入. 推荐使用第二种形式. 一.使用监听器注入 ①:创建监听器类: impor ...

随机推荐

  1. Web性能优化:What? Why? How?

    为什么要提升web性能? Web性能黄金准则:只有10%~20%的最终用户响应时间花在了下载html文档上,其余的80%~90%时间花在了下载页面组件上. web性能对于用户体验有及其重要的影响,根据 ...

  2. 《Django By Example》第二章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:翻译完第一章后,发现翻译第二章的速 ...

  3. 使用技术手段限制DBA的危险操作—Oracle Database Vault

    概述 众所周知,在业务高峰期,某些针对Oracle数据库的操作具有很高的风险,比如修改表结构.修改实例参数等等,如果没有充分评估和了解这些操作所带来的影响,这些操作很可能会导致故障,轻则导致应用错误, ...

  4. Android业务组件化之现状分析与探讨

    前言: 从个人经历来说的话,从事APP开发这么多年来,所接触的APP的体积变得越来越大,业务的也变得越来越复杂,总来来说只有一句话:这是一个APP臃肿的时代!所以为了告别APP臃肿的时代,让我们进入一 ...

  5. DDD 领域驱动设计-商品建模之路

    最近在做电商业务中,有关商品业务改版的一些东西,后端的架构设计采用现在很流行的微服务,有关微服务的简单概念: 微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成.系统中的各个微服务可被独 ...

  6. 【NLP】Python NLTK获取文本语料和词汇资源

    Python NLTK 获取文本语料和词汇资源 作者:白宁超 2016年11月7日13:15:24 摘要:NLTK是由宾夕法尼亚大学计算机和信息科学使用python语言实现的一种自然语言工具包,其收集 ...

  7. 玩转spring boot——开篇

    很久没写博客了,而这一转眼就是7年.这段时间并不是我没学习东西,而是园友们的技术提高的非常快,这反而让我不知道该写些什么.我做程序已经有十几年之久了,可以说是彻彻底底的“程序老炮”,至于技术怎么样?我 ...

  8. C#异步编程

    什么是异步编程 什么是异步编程呢?举个简单的例子: using System.Net.Http; using System.Threading.Tasks; using static System.C ...

  9. 如何用Java类配置Spring MVC(不通过web.xml和XML方式)

    DispatcherServlet是Spring MVC的核心,按照传统方式, 需要把它配置到web.xml中. 我个人比较不喜欢XML配置方式, XML看起来太累, 冗长繁琐. 还好借助于Servl ...

  10. java web学习总结(五) -------------------servlet开发(一)

    一.Servlet简介 Servlet是sun公司提供的一门用于开发动态web资源的技术. Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向 ...