【问题标题】:Finding a memory leak in a Spring MVC app在 spring mvc 应用程序中发现内存泄漏
【发布时间】:2013-12-13 15:29:48
【问题描述】:

我最近做了this tutorial 并且代码运行良好。然后,今天,我在 eclipse 中重新打开了项目并选择了 Run As...Run on Server。该应用程序似乎从 Eclipse 控制台中运行的日志中完成了正常的加载过程,但随后在 Eclipse 控制台中显示了以下错误消息,而我原本希望应用程序加载到浏览器中:

Exception in thread "http-bio-8080-exec-3" java.lang.OutOfMemoryError: PermGen space  

我之前也确实从this tutorial 运行了代码,并打开了一些 blob 文件,但我认为这不会导致问题,因为即使我关闭所有东西并在执行之前重新启动计算机,此错误仍然存​​在运行方式...再次在服务器上运行以下代码。

我在 Google 上搜索了错误和 read postings 关于内存泄漏的信息,例如将大量文件完全加载到内存中而不是使用输入流等。但是当我分析应用程序中的所有代码时,我找不到任何大的变量。我在下面发布代码。请让我知道是否还有其他内容可以帮助您找到问题。

谁能告诉我内存泄漏在哪里?

这里是链接控制器:

@Controller
public class LinkController {

    @RequestMapping(value="/")
    public ModelAndView mainPage() {return new ModelAndView("home");}

    @RequestMapping(value="/index")
    public ModelAndView indexPage() {return new ModelAndView("home");}

}

这里是团队控制器:

@Controller
@RequestMapping(value="/team")
public class TeamController {

    @Autowired
    private TeamService teamService;

    @RequestMapping(value="/add", method=RequestMethod.GET)
    public ModelAndView addTeamPage() {
            ModelAndView modelAndView = new ModelAndView("add-team-form");
            modelAndView.addObject("team", new Team());
            return modelAndView;
    }

    @RequestMapping(value="/add", method=RequestMethod.POST)
    public ModelAndView addingTeam(@ModelAttribute Team team) {
            ModelAndView modelAndView = new ModelAndView("home");
            teamService.addTeam(team);
            String message = "Team was successfully added.";
            modelAndView.addObject("message", message);
            return modelAndView;
    }

    @RequestMapping(value="/list")
    public ModelAndView listOfTeams() {
            ModelAndView modelAndView = new ModelAndView("list-of-teams");
            List<Team> teams = teamService.getTeams();
            modelAndView.addObject("teams", teams);
            return modelAndView;
    }

    @RequestMapping(value="/edit/{id}", method=RequestMethod.GET)
    public ModelAndView editTeamPage(@PathVariable Integer id) {
            ModelAndView modelAndView = new ModelAndView("edit-team-form");
            Team team = teamService.getTeam(id);
            modelAndView.addObject("team",team);
            return modelAndView;
    }

    @RequestMapping(value="/edit/{id}", method=RequestMethod.POST)
    public ModelAndView edditingTeam(@ModelAttribute Team team, @PathVariable Integer id) {
            ModelAndView modelAndView = new ModelAndView("home");       
            teamService.updateTeam(team);
            String message = "Team was successfully edited.";
            modelAndView.addObject("message", message);
            return modelAndView;
    }

    @RequestMapping(value="/delete/{id}", method=RequestMethod.GET)
    public ModelAndView deleteTeam(@PathVariable Integer id) {
            ModelAndView modelAndView = new ModelAndView("home");
            teamService.deleteTeam(id);
            String message = "Team was successfully deleted.";
            modelAndView.addObject("message", message);
            return modelAndView;
    }
}

这里是 TeamDAOImpl:

@Repository
public class TeamDAOImpl implements TeamDAO {
    @Autowired
    private SessionFactory sessionFactory;

    private Session getCurrentSession() {return sessionFactory.getCurrentSession();}

    public void addTeam(Team team) {getCurrentSession().save(team);}

    public void updateTeam(Team team) {
            Team teamToUpdate = getTeam(team.getId());
            teamToUpdate.setName(team.getName());
            teamToUpdate.setRating(team.getRating());
            getCurrentSession().update(teamToUpdate);
    }

    public Team getTeam(int id) {
            Team team = (Team) getCurrentSession().get(Team.class, id);
            return team;
    }

    public void deleteTeam(int id) {
            Team team = getTeam(id);
            if (team != null){getCurrentSession().delete(team);}
    }

    @SuppressWarnings("unchecked")
    public List<Team> getTeams() {
            return getCurrentSession().createQuery("from Team").list();
    }

}

这里是初始化器:

public class Initializer implements WebApplicationInitializer {
    public void onStartup(ServletContext servletContext)throws ServletException {
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            ctx.register(WebAppConfig.class);
            servletContext.addListener(new ContextLoaderListener(ctx));
            ctx.setServletContext(servletContext);
            Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
            servlet.addMapping("/");
            servlet.setLoadOnStartup(1);
    }
}  

这里是 WebAppConfig:

@Configuration
@ComponentScan("com.sprhib")
@EnableWebMvc
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class WebAppConfig {
    private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
    private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
    private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
    private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
    private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
    private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
    private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";

    @Resource
    private Environment env;

    @Bean
    public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
            dataSource.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
            dataSource.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
            dataSource.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
            return dataSource;
    }

    @Bean
    public LocalSessionFactoryBean sessionFactory() {
            LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
            sessionFactoryBean.setDataSource(dataSource());
            sessionFactoryBean.setPackagesToScan(env.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
            sessionFactoryBean.setHibernateProperties(hibProperties());
            return sessionFactoryBean;
    }

    private Properties hibProperties() {
            Properties properties = new Properties();
            properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
            properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
            return properties;        
    }

    @Bean
    public HibernateTransactionManager transactionManager() {
            HibernateTransactionManager transactionManager = new HibernateTransactionManager();
            transactionManager.setSessionFactory(sessionFactory().getObject());
            return transactionManager;
    }

    @Bean
    public UrlBasedViewResolver setupViewResolver() {
            UrlBasedViewResolver resolver = new UrlBasedViewResolver();
            resolver.setPrefix("/WEB-INF/pages/");
            resolver.setSuffix(".jsp");
            resolver.setViewClass(JstlView.class);
            return resolver;
    }
}  

这里是团队:

@Entity
@Table(name="teams")
public class Team {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;
    private Integer rating;
    public Integer getId() {return id;}
    public void setId(Integer id) {this.id = id;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public Integer getRating() {return rating;}
    public void setRating(Integer rating) {this.rating = rating;}
}  

这里是 TeamServiceImpl:

@Service
@Transactional
public class TeamServiceImpl implements TeamService {
    @Autowired
    private TeamDAO teamDAO;

    public void addTeam(Team team) {teamDAO.addTeam(team);}

    public void updateTeam(Team team) {teamDAO.updateTeam(team);}

    public Team getTeam(int id) {return teamDAO.getTeam(id);}

    public void deleteTeam(int id) {teamDAO.deleteTeam(id);}

    public List<Team> getTeams() {return teamDAO.getTeams();}

}  

编辑:

如果我应该设置 JAVA_OPTS 变量以允许类卸载并增加内存大小,我该如何在运行 tomcat 7 的 Windows 7 中这样做?

我的感觉是我需要创建一个 Windows 系统变量,然后可能在命令行上运行一些东西。但是什么?这是我的开始:

JAVA_OPTS="-XX:MaxPermSize=128M -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled -Xms256m -Xmx512m"

【问题讨论】:

  • 服务器启动时 -XX:MaxPermSize 的值是多少?
  • @benjamin.d 我不知道。我怎样才能知道?

标签: java eclipse spring hibernate memory-leaks


【解决方案1】:

如果你想在Windows上更改你的tomcat实例的JAVA_OPTS,你需要编辑文件catalina.bat通常在TOMCAT_BASE/bin/catalina.bat

但是,我会调整您指定的选项。首先,如果指定-XX:+CMSClassUnloadingEnabled,则不需要-XX:+CMSPermGenSweepingEnabled

其次,要使-XX:+CMSClassUnloadingEnabled 生效,您还必须指定-XX:+UseConcMarkSweepGC

因此,总而言之,编辑您的 TOMCAT_BASE/bin/catalina.bat 并在脚本开头的大量 rem 语句下(大约在第 99 行左右),添加以下行:

set JAVA_OPTS=-XX:MaxPermSize=128M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -Xms256m -Xmx512m

请记住,如果您需要更多 PermGen 空间,请将 MaxPermSize 增加到更高的值,但是,如果没有垃圾收集选项(UseConcMarkSweepGCCMSClassUnloadingEnabled),您只是延长不可避免的java.lang.OutOfMemoryError: PermGen space

希望有帮助

【讨论】:

    【解决方案2】:

    您的应用程序中实际上可能没有内存泄漏。当您多次重新部署应用程序时,服务器会出现问题,最终会导致内存不足问题。不幸的是,此时您所能做的就是重新启动所有内容,您的应用应该可以正常运行。

    查看this question,它可以帮助您增加 Java 的可用内存,以便您在遇到此问题之前可以工作更长时间。

    This questionthis answer 也可能会有所帮助。

    【讨论】:

    • 你是说我应该在命令行运行一些东西吗?如果是这样,我应该采取哪些具体步骤?创建系统变量和运行命令行代码是我可以做的,但对我来说是新的,所以我需要明确的步骤。这是我从链接中得到的: JAVA_OPTS -XX:MaxPermSize=128M -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
    • 您可以通过多种方式设置 JAVA_OPTS。 This page 显示了 Tomcat 中的一个示例,但通过 Google 快速搜索可以找到更多适用于不同操作系统和环境的选项。
    • 我在上面的原始帖子中添加了一个编辑,指定了我要如何设置 JAVA_OPTS 变量。我正在运行 Windows 7 和 Tomcat 7。在这种环境下我具体应该做什么?
    • 尝试查看advice here,尤其是这个blog post 一步一步来。
    • 如果有人可以在几行代码中展示解决方案,那么该人将获得 +1 并归功于答案。
    猜你喜欢
    • 2015-06-04
    • 1970-01-01
    • 2016-03-28
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多