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();}
}
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"
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With