src/main/java/js/container/Container.java
public void destroy() {
		log.trace("destroy()");

		// cron manager should be destroyed first to avoid invoking cron methods on cleaned managed instance
		cronManager.destroy();

		// classes pool is not sorted; it is a hash map for performance reasons
		// also, a managed class may appear multiple times if have multiple interfaces
		// bellow sorted set is used to ensure reverse order on managed classes destruction
		// comparison is based on managed class key that is created incrementally

		Set<ManagedClassSPI> sortedClasses = new TreeSet<>(new Comparator<ManagedClassSPI>() {
			@Override
			public int compare(ManagedClassSPI o1, ManagedClassSPI o2) {
				// compare second with first to ensure descending sorting
				return o2.getKey().compareTo(o1.getKey());
			}
		});

		for (ManagedClassSPI managedClass : classesPool.values()) {
			// process only implementations of managed pre-destroy interface
			if (Types.isKindOf(managedClass.getImplementationClass(), ManagedPreDestroy.class)) {
				sortedClasses.add(managedClass);
			}
		}

		for (ManagedClassSPI managedClass : sortedClasses) {
			ScopeFactory scopeFactory = scopeFactories.get(managedClass.getInstanceScope());
			InstanceKey instanceKey = new InstanceKey(managedClass.getKey());
			Object instance = scopeFactory.getInstance(instanceKey);
			if (instance == null) {
				continue;
			}

			// sorted managed classes contains only implementations of pre-destroy interface
			// in case instance is a Java Proxy takes care to execute pre-destroy hook on wrapped instance
			// in order to avoid adding container services to this finalization hook
			ManagedPreDestroy managedInstance = (ManagedPreDestroy) Classes.unproxy(instance);
			log.debug("Pre-destroy managed instance |%s|.", managedInstance.getClass());
			try {
				managedInstance.preDestroy();
			} catch (Throwable t) {
				log.dump(String.format("Managed instance |%s| pre-destroy fail:", instance.getClass()), t);
			}
		}

		for (ScopeFactory scopeFactory : scopeFactories.values()) {
			// local scope has null factory
			if (scopeFactory != null) {
				scopeFactory.clear();
			}
		}

		classesPool.clear();
		instanceProcessors.clear();
		scopeFactories.clear();
		instanceFactories.clear();
	}
public void destroy() {
		log.trace("destroy()");

		// cron manager should be destroyed first to avoid invoking cron methods on cleaned managed instance
		cronManager.destroy();

		// classes pool is not sorted; it is a hash map for performance reasons
		// also, a managed class may appear multiple times if have multiple interfaces
		// bellow sorted set is used to ensure reverse order on managed classes destruction
		// comparison is based on managed class key that is created incrementally

		Set<ManagedClassSPI> sortedClasses = new TreeSet<>(new Comparator<ManagedClassSPI>() {
			@Override
			public int compare(ManagedClassSPI o1, ManagedClassSPI o2) {
				// compare second with first to ensure descending sorting
				return o2.getKey().compareTo(o1.getKey());
			}
		});

		for (ManagedClassSPI managedClass : classesPool.values()) {
			// process only implementations of managed pre-destroy interface
			if (Types.isKindOf(managedClass.getImplementationClass(), ManagedPreDestroy.class)) {
				sortedClasses.add(managedClass);
			}
		}

		for (ManagedClassSPI managedClass : sortedClasses) {
			ScopeFactory scopeFactory = scopeFactories.get(managedClass.getInstanceScope());
			InstanceKey instanceKey = new InstanceKey(managedClass.getKey());
			Object instance = scopeFactory.getInstance(instanceKey);
			if (instance == null) {
				continue;
			}

			// sorted managed classes contains only implementations of pre-destroy interface
			// in case instance is a Java Proxy takes care to execute pre-destroy hook on wrapped instance
			// in order to avoid adding container services to this finalization hook
			ManagedPreDestroy managedInstance = (ManagedPreDestroy) Classes.unproxy(instance);
			log.debug("Pre-destroy managed instance |%s|.", managedInstance.getClass());
			try {
				managedInstance.preDestroy();
			} catch (Throwable t) {
				log.dump(String.format("Managed instance |%s| pre-destroy fail:", instance.getClass()), t);
			}
		}

		for (ScopeFactory scopeFactory : scopeFactories.values()) {
			// local scope has null factory
			if (scopeFactory != null) {
				scopeFactory.clear();
			}
		}

		classesPool.clear();
		instanceProcessors.clear();
		scopeFactories.clear();
		instanceFactories.clear();
	}
Destroy container and release caches, factories and processors. This is container global clean-up invoked at application
unload; after executing this method no managed instance can be created or reused. Attempting to use {@link AppFactory}
after container destroy may lead to not predictable behavior.
<p>
Before caches clean-up, this method invokes {@link ManagedPreDestroy} on all managed instances that are active, that is,
cached in all scope factories. Takes care to execute pre-destroy in reverse order from application descriptor
declarations. This ensure {@link App} is destroyed last since App class descriptor is declared first into application
descriptor.