ฉันมีแอป 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 ขัดข้อง
ฉันมีบันทึก VehicleEntity ประมาณ 3200 รายการ (สมมติว่าเป็น 3200 พอดี) ดังนั้นฉันจึงค้นหา VehicleDataUpdater - มีวัตถุอยู่ในหน่วยความจำจำนวนเท่าใด หลังจากการวนซ้ำครั้งแรก (เมื่อฉันเพิ่งเริ่มแอป) มันน้อยกว่า 3200 แต่ไม่ใช่ศูนย์ - อาจจะประมาณ 3,000-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()));
}
}
ยานพาหนะดาว:
@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 ที่จัดการด้วยสปริงโดยมี@Scheduled
เพื่อให้ Spring ฉีดการขึ้นต่อกันและใช้@Scheduled
เพื่อกำหนดเวลาสิ่งต่าง ๆ คุณควรทำงานกับกรอบงานที่คุณกำลังทำงานอยู่ เกี่ยวกับชิ้นส่วนต่างๆ นั่นอาจไม่ได้ผลทั้งหมดกับโซลูชันปัจจุบันของคุณ คุณต้องการงานเล็กๆ เหล่านั้นทั้งหมดด้วยหรือเปล่า? ทำไมไม่ทำการอัพเดตตามลำดับล่ะ? - person M. Deinum   schedule 23.04.2020@Scheduled
และอ่านจากไฟล์คุณสมบัติได้ ดังนั้นจึงไม่มีอะไรขัดขวางคุณจากการออกแบบที่เหมาะสม - person M. Deinum   schedule 23.04.2020