У меня есть приложение Spring Boot, работающее на Wildfly 18.0.1. Основная цель приложения: каждые 5 минут запускать какое-то задание. Итак, я делаю:
TaskScheduler: инициализировать планировщик
@Autowired
ThreadPoolTaskScheduler taskScheduler;
taskScheduler.scheduleWithFixedDelay(new ScheduledVehicleDataUpdate(), 300000);
ScheduledVehicleDataUpdate: планировщик, запускающий программу обновления.
public class ScheduledVehicleDataUpdate implements Runnable {
@Autowired
TaskExecutor taskExecutor;
@Override
public void run() {
try {
CountDownLatch countDownLatch;
List<VehicleEntity> vehicleList = VehicleService.getInstance().getList();
if (vehicleList.size() > 0) {
countDownLatch = new CountDownLatch(vehiclesList.size());
vehicleList.forEach(vehicle -> taskExecutor.execute(new VehicleDataUpdater(vehicle, countDownLatch)));
countDownLatch.await();
}
}
catch (InterruptedException | RuntimeException e) {
System.out.println(e.getMessage())
}
}
}
Исполнитель задач:
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(23);
executor.setMaxPoolSize(23);
executor.setQueueCapacity(5000);
executor.setThreadNamePrefix("VehicleService_updater_thread");
executor.initialize();
return executor;
}
VehicleDataUpdater: основной класс средства обновления.
public class VehicleDataUpdater implements Runnable {
private final VehicleEntity vehicle;
private final CountDownLatch countDownLatch;
public VehicleDataUpdater(VehicleEntity vehicle, CountDownLatch countDownLatch) {
this.vehicle = vehicle;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
this.updateVehicleData();
}
catch (Exception e) {
System.out.println(e.getMessage());
}
finally {
countDownLatch.countDown();
}
}
public void updateVehicleData() {
// DO UPDATE ACTIONS;
}
}
Проблема в том, что после завершения ScheduledVehicleDataUpdate память НЕ очищается. Это выглядит так:
С каждым шагом память растет, растет, растет и в непредсказуемый момент вся память освобождается. И объекты из первой итерации, и объекты из последней итерации. В самом плохом случае занимает всю доступную память (120Gb) и Wildfly падает.
У меня около 3200 записей VehicleEntity (допустим, ровно 3200). Поэтому я искал VehicleDataUpdater - сколько объектов в памяти. После первой итерации (когда я только запустил приложение) это меньше 3200, но не ноль - может быть около 3000-3100. И с каждым шагом он растет, но не точно на 3200 записей. Это означает, что часть объектов стирается из памяти, но большая часть остается там.
Далее: нормальная продолжительность итерации составляет около 30 секунд - 1 минута. Когда память не очищается и продолжает расти, то с каждой итерацией становится все больше и больше времени: самое длинное, что я видел, было 30 минут. И потоки из пула в основном находятся в состоянии «монитор», т.е. есть некоторые блокировки, ожидающие освобождения. Возможно, блокировки от предыдущих итераций, которые не были освобождены - и снова вопрос - почему вся память не была освобождена на предыдущем шаге?
Если я выполняю обновление в одном потоке (без taskExecutor, просто vehicleList.foreach(vehicle -> VehicleDataUpdater(vehicle));), то я не вижу роста памяти. После обновления память каждого автомобиля очищается.
Я не обнаружил никаких проблем с утечками памяти для ThreadPoolTaskExecutor или ThreadPoolTaskScheduler, поэтому понятия не имею, как это исправить.
Какие возможные способы не очищать память после завершения задачи планировщика? Как я могу посмотреть, кто блокирует объект после завершения? Я использую VisualVM 2.0.1 и не нашел там таких возможностей.
ИЗМЕНИТЬ 1:
Автосервис:
public class VehicleService {
private static VehicleService instance = null;
private VehicleDao dao;
public static VehicleService getInstance(){
if (instance == null) {
instance = new VehicleService();
}
return instance;
}
private VehicleService(){}
public void setDao(VehicleDao vehicleDao) { this.dao = vehicleDao; }
public List<VehicleEntity> list() {
return new ArrayList<>(this.dao.list(LocalDateTime.now()));
}
}
VehicleDao:
@Repository
public class VehicleDao {
@PersistenceContext(unitName = "entityManager")
private EntityManager entityManager;
@Transactional("transactionManager")
public List<VehicleRegisterEntity> list(LocalDateTime dtPeriod) {
return this.entityManager.createQuery("SOME_QUERY", VehicleEntity.class).getResultList();
}
}
ИнитСервис:
@Service
public class InitHibernateService {
private final VehicleDao vehicleDao;
@Autowired
public InitHibernateService(VehicleDao vehicleDao){
this.vehicleDao = vehicleDao;
}
@PostConstruct
private void setDao() {
VehicleService.getInstance().setDao(this.vehicleDao);
}
}
Диспетчер объектов:
@Bean(name = "entityManager")
@DependsOn("dataSource")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceProviderClass(HibernatePersistenceProvider.class);
em.setDataSource(dataSource());
em.setPackagesToScan("MY_PACKAGE");
em.setJpaVendorAdapter(vendorAdapter());
em.setJpaProperties(hibernateProperties());
em.setPersistenceUnitName("customEntityManager");
em.setJpaDialect(new CustomHibernateJpaDialect());
return em;
}
VehicleService.getInstance().getList()
? Также вам, вероятно, следует обновлять/читать информацию по частям/лениво вместо списков. Предполагая, что вы используете что-то вроде JPA, у вас может возникнуть другая проблема с отсоединенными объектами. В общем, в вашем вопросе недостаточно информации для ответа на него. - person M. Deinum   schedule 23.04.2020ScheduledVehicleDataUpdate
должен быть управляемым bean-компонентом spring с@Scheduled
, чтобы Spring внедрял зависимости и использовал@Scheduled
для планирования. Вы должны работать с фреймворком, над которым вы сейчас работаете. Что касается фрагментов, это может не полностью работать с вашим текущим решением. Вам также нужны все эти мелкие задачи? Почему бы просто не делать обновления последовательно? - person M. Deinum   schedule 23.04.2020@Scheduled
и читать из файла свойств, поэтому ничто не мешает вам сделать правильный дизайн. - person M. Deinum   schedule 23.04.2020