Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

finding memory leak in spring mvc app

I recently did this tutorial and got the code running fine. Then, today, I reopened the project in Eclipse and chose Run As...Run on Server. The application seemed to go through its normal load process from the logs running in the Eclipse console, but then the following error message showed up in the Eclipse console at the point when I would have expected the application to load in the browser instead:

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

I did also run the code from this tutorial beforehand, and opened a few blob files, but I don't think that caused the problem because this error persists even when I shut everything down and restart the computer before doing Run As...Run on Server for the code below again.

I searched for the error and read posts about memory leaks from things like loading a massive file completely into memory instead of using an input stream, etc. But when I analyzed all the code in the application, I could not find any large variables. I am posting the code below.

How can I find the memory leak?

Here is Link Controller:

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

}

Here is Team Controller:

@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;
    }
}

Here is 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();
    }

}

Here is Initializer:

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);
    }
}  

Here is 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;
    }
}  

Here is Team:

@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;}
}  

Here is 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();}

}  

Edit

If I am supposed to set the JAVA_OPTS variable to allow class unloading, and to increase memory size, how do I do so in Windows 7 running Tomcat 7?

My sense is I need to create a Windows system variable and possibly run something on the command line after. But what? Here is what I am starting with:

JAVA_OPTS="-XX:MaxPermSize=128M -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled -Xms256m -Xmx512m"
like image 981
CodeMed Avatar asked Nov 27 '13 19:11

CodeMed


1 Answers

If you want to change the JAVA_OPTS of your tomcat instance on Windows, you need to edit the file catalina.bat usually in TOMCAT_BASE/bin/catalina.bat.

BUT, I would tweak the options you specified. Firstly, if you specify -XX:+CMSClassUnloadingEnabled, you don't need -XX:+CMSPermGenSweepingEnabled.

Secondly, for -XX:+CMSClassUnloadingEnabled to have any effect, you ALSO must specify -XX:+UseConcMarkSweepGC.

So, in summary, edit your TOMCAT_BASE/bin/catalina.bat and under the huge block of rem statements at the beginning of the script (somewhere around line 99 ish), add this line:

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

And remember, if you need more PermGen space, increase MaxPermSize to something higher however, without the garbage collection options (UseConcMarkSweepGC and CMSClassUnloadingEnabled) you're just prolonging the inevitable java.lang.OutOfMemoryError: PermGen space

Hope that helps

like image 167
Will Warren Avatar answered Oct 16 '22 21:10

Will Warren