我正在构建一个基于 Java 1.8、spring、hibernate、mysql 并使用 tomcat 作为 web 服务器的 web 应用程序。现在我的应用程序正在记录(@5 秒间隔)覆盆子的系统数据,例如;cpu温度,cpu核心电压,已用内存,可用内存,内存缓冲区等。问题是应用程序有时会因为内存不足而崩溃,我无法确定原因。
首先,在 tomcat 上,我在 tomcat 的启动服务中有以下几行:
Environment='CATALINA_OPTS=-Xms256M -Xmx512M -server -XX:+UseParallelGC'
Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom'
它在启动tomcat后大约8小时内崩溃。我删除了这些行,现在它在大约 2 天后崩溃,因为它内存不足。今天这是第一次在启动脚本中没有这些行的情况下崩溃,我在 db 中插入的最后一行是:
# id, cpuTemperature, cpuCoreVoltage, usedMemory, freeMemory, memoryBuffers, sdramCVoltage, sdramIVoltage, sdramPVoltage, createdAt, modifiedAt, createdBy, modifiedBy
'52414', '39', '1.3125', '958320640', '12447744', '92495872', '1.2', '1.2', '1.225', '2015-11-23 15:53:58', NULL, NULL, NULL
对于此信息,我正在使用 pi4j 库中的 SystemInfo 类。getMemoryUsed() 以位为单位返回已用内存,因此经过简单的数学运算后,我们获得了 913.92 Mb 的已用内存。
当应用程序启动时,它仅消耗 250 Mb 内存,3 天后应用程序崩溃,因为它达到了树莓派的最大内存。
春季应用配置:
@Configuration
@EnableWebMvc
@EnableScheduling
@ComponentScan({"ro.i2cWebAppController.app.controller","ro.i2cWebAppController.app.serv","ro.i2cWebAppController.app.schedule"})
@Import({SpringSecurityConfig.class})
@EnableJpaRepositories({"ro.i2cWebAppController.app.repo"})
public class AppConfig extends WebMvcConfigurerAdapter {
private static final Logger logger = Logger.getLogger(AppConfig.class);
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/res/**").addResourceLocations("/WEB-RES/");
registry.addResourceHandler("/**/**.ico").addResourceLocations("/WEB-RES/img/favicon.ico");
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Bean
public ViewResolver viewResolver(ContentNegotiationManager manager) {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("views/");
resolver.setSuffix(".jsp");
return resolver;
}
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean sessionFactory() {
LocalContainerEntityManagerFactoryBean lcemfb = new LocalContainerEntityManagerFactoryBean();
HibernateJpaVendorAdapter hjva = new HibernateJpaVendorAdapter();
hjva.setShowSql(true);
//persistence unit
lcemfb.setDataSource(dataSource());
lcemfb.setJpaVendorAdapter(hjva);
lcemfb.setPackagesToScan("ro.i2cWebAppController.app.model");
lcemfb.setJpaPropertyMap(hibernateProperties());
return lcemfb;
}
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
if(isUnix()){
dataSource.setUrl("jdbc:mysql://localhost:3306/i2cwac?autoReconnect=true");
}else{
dataSource.setUrl("jdbc:mysql://192.168.1.30:3306/i2cwac?autoReconnect=true");
}
dataSource.setUsername("i2cwac");
dataSource.setPassword("Stefan1234");
dataSource.setInitialSize(5);
dataSource.setMaxTotal(20);
dataSource.setMaxIdle(10);
dataSource.setMinIdle(5);
dataSource.setMaxWaitMillis(2000);
return dataSource;
}
@Bean
@Autowired
public JpaTransactionManager transactionManager(EntityManagerFactory lcemfb) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(lcemfb);
return txManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
Map<String,String> hibernateProperties() {
Map<String,String> propMap = new HashMap<>();
propMap.put("hibernate.hbm2ddl.auto", "validate");
propMap.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
propMap.put("hibernate.globally_quoted_identifiers", "true");
propMap.put("hibernate.show_sql", "true");
return propMap;
}
@Bean
public Integer ambientTemperature(){
return new Integer(0);
}
@Bean
public SystemInformationService systemInformationService(){
return new SystemInformationServiceImpl();
}
@Bean
public Boolean isUnix(){
if(System.getProperty("os.name").toLowerCase().indexOf("win")>=0){
logger.info("isUnix : false because : " + System.getProperty("os.name"));
return false;
}else{
logger.info("isUnix : true because : " + System.getProperty("os.name"));
return true;
}
}
@Bean
public I2CBus i2CBus(){
I2CBus i2CBus = null;
if (isUnix()) {
int count = 0;
int maxTries = 10;
while (true) {
try {
i2CBus = I2CFactory.getInstance(I2CBus.BUS_1);
break;
} catch (IOException e) {
++count;
logger.error("I2CBUS try " + count);
if (count == maxTries) e.printStackTrace();
}
}
}
if (i2CBus == null){
logger.info("I2CBUS is NULL");
}else{
logger.info("Got I2CBUS " + i2CBus.toString());
}
return i2CBus;
}
}
调度器类:
@Component
public class RaspberryPiScheduler {
@Autowired
SystemInformationService systemInformationService;
@Autowired
RaspberryPiDataService raspberryPiDataService;
@Autowired
Boolean isUnix;
private static final Logger logger = Logger.getLogger(RaspberryPiScheduler.class);
/**
* Scheduled method to write data from Raspberry Pi sensors
*/
@Scheduled(fixedDelay = 5000)
public void writeRaspberryPiData(){
logger.info("Insert raspberrypi stats");
if(isUnix){
try {
raspberryPiDataService.save(new RaspberryPiData(
systemInformationService.getCpuTemperature(),
systemInformationService.getCpuVoltage(),
systemInformationService.getMemoryUsed(),
systemInformationService.getMemoryFree(),
systemInformationService.getMemoryBuffers(),
systemInformationService.getMemoryVoltageSDRam_C(),
systemInformationService.getMemoryVoltageSDRam_I(),
systemInformationService.getMemoryVoltageSDRam_P()
));
} catch (Exception e) {
logger.error("writeRaspberryPiData error",e);
}
}
}
}
基础回购:
@NoRepositoryBean
public interface BaseRepo<T, ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
}
现在我在日志中没有找到错误,但过去它因 SIGILL 错误而崩溃。找到日志后,我将编辑这篇文章。
我发现来自 JVM 的错误日志:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGILL (0x4) at pc=0x741db028, pid=1918, tid=1620767840
#
# JRE version: Java(TM) SE Runtime Environment (8.0-b132) (build 1.8.0-b132)
# Java VM: Java HotSpot(TM) Server VM (25.0-b70 mixed mode linux-arm )
# Problematic frame:
# J 1635 C2 com.mysql.jdbc.ResultSetImpl.<init>(JJLcom/mysql/jdbc/MySQLConnection;Lcom/mysql/jdbc/StatementImpl;)V (314 bytes) @ 0x741db028 [0x741dae20+0x208]
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# If you would like to submit a bug report, please visit:
# http://bugreport.sun.com/bugreport/crash.jsp
#
--------------- T H R E A D ---------------
Current thread (0x63003c00): JavaThread "pool-2-thread-2" [_thread_in_Java, id=1967, stack(0x60960000,0x609b0000)]
siginfo:si_signo=SIGILL: si_errno=0, si_code=1 (ILL_ILLOPC), si_addr=0x741db028
Registers:
r0 = 0x71897568
r1 = 0x00000008
r2 = 0x00000000
r3 = 0x00000000
r4 = 0x62922d40
r5 = 0x00000368
r6 = 0x693d65e8
r7 = 0x62922d40
r8 = 0x73a51000
r9 = 0x66c13e30
r10 = 0x63003c00
fp = 0x693d65e8
r12 = 0x718974b8
sp = 0x609adfa0
lr = 0x73e74bb0
pc = 0x741db028
cpsr = 0x60000010
Top of Stack: (sp=0x609adfa0)
0x609adfa0: 6e614d79 3d74696d 00000007 00000000
0x609adfb0: 609ae008 73e74bb0 00000000 00000000
0x609adfc0: 693d65e8 00000000 00000000 00000000
0x609adfd0: 693d65e8 00000000 00000000 00000000
0x609adfe0: 00000000 718974b8 609adfe8 629d07b7
0x609adff0: 609ae028 629d4420 602ed004 629d0800
0x609ae000: 609adfcc 609ae010 609ae04c 73e74bb0
0x609ae010: 00000000 693d65e8 00000000 00000000
Instructions: (pc=0x741db028)
0x741db008: e5d45008 e59cb05c e5cc5044 f57ff05f
0x741db018: e59b4004 e1540007 1a00003a e3005368
0x741db028: e18b40d5 e59c605c e1cc40f8 f57ff05f
0x741db038: e5964004 e1540007 1a000036 e5964478
Register to memory mapping:
r0 = 0x71897568
0x71897568 is an oop
[Lcom.mysql.jdbc.Field;
- klass: 'com/mysql/jdbc/Field'[]
- length: 0
r1 = 0x00000008
0x00000008 is an unknown value
r2 = 0x00000000
0x00000000 is an unknown value
r3 = 0x00000000
0x00000000 is an unknown value
r4 = 0x62922d40
0x62922d40 is an unknown value
r5 = 0x00000368
0x00000368 is an unknown value
r6 = 0x693d65e8
0x693d65e8 is an oop
com.mysql.jdbc.JDBC4Connection
- klass: 'com/mysql/jdbc/JDBC4Connection'
r7 = 0x62922d40
0x62922d40 is an unknown value
r8 = 0x73a51000
0x73a51000 is an unallocated location in the heap
r9 = 0x66c13e30
0x66c13e30 is an unknown value
r10 = 0x63003c00
0x63003c00 is a thread
fp = 0x693d65e8
0x693d65e8 is an oop
com.mysql.jdbc.JDBC4Connection
- klass: 'com/mysql/jdbc/JDBC4Connection'
r12 = 0x718974b8
0x718974b8 is an oop
com.mysql.jdbc.JDBC42ResultSet
- klass: 'com/mysql/jdbc/JDBC42ResultSet'
sp = 0x609adfa0
0x609adfa0 is pointing into the stack for thread: 0x63003c00
lr = 0x73e74bb0
0x73e74bb0 is at code_begin+2176 in an Interpreter codelet
invoke return entry points [0x73e74330, 0x73e74cc0] 2448 bytes
pc = 0x741db028
0x741db028 is at entry_point+520 in (nmethod*)0x741dad08
Stack: [0x60960000,0x609b0000], sp=0x609adfa0, free space=311k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
J 1635 C2 com.mysql.jdbc.ResultSetImpl.<init>(JJLcom/mysql/jdbc/MySQLConnection;Lcom/mysql/jdbc/StatementImpl;)V (314 bytes) @ 0x741db028 [0x741dae20+0x208]
j com.mysql.jdbc.JDBC4ResultSet.<init>(JJLcom/mysql/jdbc/MySQLConnection;Lcom/mysql/jdbc/StatementImpl;)V+7
j com.mysql.jdbc.JDBC42ResultSet.<init>(JJLcom/mysql/jdbc/MySQLConnection;Lcom/mysql/jdbc/StatementImpl;)V+7
j sun.reflect.GeneratedConstructorAccessor17.newInstance([Ljava/lang/Object;)Ljava/lang/Object;+242
J 1634 C2 com.mysql.jdbc.ResultSetImpl.getInstance(JJLcom/mysql/jdbc/MySQLConnection;Lcom/mysql/jdbc/StatementImpl;)Lcom/mysql/jdbc/ResultSetImpl; (65 bytes) @ 0x741e0260 [0x741dff70+0x2f0]
j com.mysql.jdbc.MysqlIO.buildResultSetWithUpdates(Lcom/mysql/jdbc/StatementImpl;Lcom/mysql/jdbc/Buffer;)Lcom/mysql/jdbc/ResultSetImpl;+170
j com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(Lcom/mysql/jdbc/StatementImpl;IIIZLjava/lang/String;Lcom/mysql/jdbc/Buffer;ZJ[Lcom/mysql/jdbc/Field;)Lcom/mysql/jdbc/ResultSetImpl;+18
j com.mysql.jdbc.MysqlIO.readAllResults(Lcom/mysql/jdbc/StatementImpl;IIIZLjava/lang/String;Lcom/mysql/jdbc/Buffer;ZJ[Lcom/mysql/jdbc/Field;)Lcom/mysql/jdbc/ResultSetImpl;+30
j com.mysql.jdbc.MysqlIO.sqlQueryDirect(Lcom/mysql/jdbc/StatementImpl;Ljava/lang/String;Ljava/lang/String;Lcom/mysql/jdbc/Buffer;IIIZLjava/lang/String;[Lcom/mysql/jdbc/Field;)Lcom/mysql/jdbc/ResultSetInternalMethods;+806
J 1596 C2 com.mysql.jdbc.ConnectionImpl.execSQL(Lcom/mysql/jdbc/StatementImpl;Ljava/lang/String;ILcom/mysql/jdbc/Buffer;IIZLjava/lang/String;[Lcom/mysql/jdbc/Field;Z)Lcom/mysql/jdbc/ResultSetInternalMethods; (426 bytes) @ 0x741cb6d8 [0x741cb3f0+0x2e8]
j com.mysql.jdbc.ConnectionImpl.setAutoCommit(Z)V+147
j org.apache.commons.dbcp2.DelegatingConnection.setAutoCommit(Z)V+9
j org.apache.commons.dbcp2.DelegatingConnection.setAutoCommit(Z)V+9
j org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor.begin()V+13
j org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.begin()V+12
j org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.begin()V+8
j org.hibernate.engine.transaction.internal.TransactionImpl.begin()V+47
j org.hibernate.internal.SessionImpl.beginTransaction()Lorg/hibernate/Transaction;+41
j org.hibernate.jpa.internal.TransactionImpl.begin()V+42
j org.springframework.orm.jpa.DefaultJpaDialect.beginTransaction(Ljavax/persistence/EntityManager;Lorg/springframework/transaction/TransactionDefinition;)Ljava/lang/Object;+26
j org.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(Ljavax/persistence/EntityManager;Lorg/springframework/transaction/TransactionDefinition;)Ljava/lang/Object;+34
j org.springframework.orm.jpa.JpaTransactionManager.doBegin(Ljava/lang/Object;Lorg/springframework/transaction/TransactionDefinition;)V+148
j org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(Lorg/springframework/transaction/TransactionDefinition;)Lorg/springframework/transaction/TransactionStatus;+204
j org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(Lorg/springframework/transaction/PlatformTransactionManager;Lorg/springframework/transaction/interceptor/TransactionAttribute;Ljava/lang/String;)Lorg/springframework/transaction/interceptor/TransactionAspectSupport$TransactionInfo;+37
j org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(Ljava/lang/reflect/Method;Ljava/lang/Class;Lorg/springframework/transaction/interceptor/TransactionAspectSupport$InvocationCallback;)Ljava/lang/Object;+49
j org.springframework.transaction.interceptor.TransactionInterceptor.invoke(Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object;+40
J 1449 C2 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()Ljava/lang/Object; (107 bytes) @ 0x74187c50 [0x74187b90+0xc0]
j org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object;+1
J 1449 C2 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()Ljava/lang/Object; (107 bytes) @ 0x74187c50 [0x74187b90+0xc0]
j org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object;+89
J 1449 C2 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()Ljava/lang/Object; (107 bytes) @ 0x74187c50 [0x74187b90+0xc0]
j org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object;+18
J 1449 C2 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()Ljava/lang/Object; (107 bytes) @ 0x74187c50 [0x74187b90+0xc0]
j org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;+321
j com.sun.proxy.$Proxy67.save(Ljava/lang/Object;)Ljava/lang/Object;+16
j ro.i2cWebAppController.app.serv.BaseServiceImpl.save(Lro/i2cWebAppController/app/model/BaseModel;)Lro/i2cWebAppController/app/model/BaseModel;+5
j ro.i2cWebAppController.app.schedule.RaspberryPiScheduler.writeRaspberryPiData()V+116
j sun.reflect.GeneratedMethodAccessor64.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;+36
J 580 C2 java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; (62 bytes) @ 0x73ffafac [0x73ffaf10+0x9c]
j org.springframework.scheduling.support.ScheduledMethodRunnable.run()V+19
j org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run()V+4
j java.util.concurrent.Executors$RunnableAdapter.call()Ljava/lang/Object;+4
j java.util.concurrent.FutureTask.runAndReset()Z+47
j java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(Ljava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;)Z+1
j java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run()V+37
j java.util.concurrent.ThreadPoolExecutor.runWorker(Ljava/util/concurrent/ThreadPoolExecutor$Worker;)V+95
j java.util.concurrent.ThreadPoolExecutor$Worker.run()V+5
j java.lang.Thread.run()V+11
v ~StubRoutines::call_stub
C 0x609aed68
我发现了一个非常奇怪的行为。在遵循评论的建议后,我在 tomcat 启动脚本中添加了以下几行:
Environment='CATALINA_OPTS=-Xms64M -Xmx192M -server -XX:+UseParallelGC -XX:+UnlockCommercialFeatures -XX:+FlightRecorder'
Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom -Dcom.sun.management.jmxremote.port=7091 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false'
我将 Java Mission Control 和 Flight Recording 连接到 raspberry 的 JVM。奇怪的是,经过 12 小时的监控,JVM 没有崩溃,一切正常。使用“XX:+UnlockCommercialFeatures -XX:+FlightRecorder”和“-Dcom.sun.management.jmxremote.port=7091 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote”重新启动服务器后.ssl=false" removerd,JVM 在 7 小时后崩溃。
通过连接到 JVM 的 Mission Control 和 Flight Recording,我还注意到 Committed Heap 一直在上升,但从未下降。