John,
As you can see on your log trace, each session contains a timer that expires when the session maxInactiveInterval is reached. When the timer expires, that session is queued for attention by the scavenger. By default the scavenger thread only runs once every 10mins, so it is timing dependent exactly when the session will be scavenged wrt when the session expires. Note that an expired session that has not yet been scavenged is not able to be used, as you have discovered. The servlet spec does not stipulate any relationship between when a session expires and when the sessionDestroyed listeners will be called, only that an expired session cannot be used, and that the listener must be called when the session is actually invalidated (expiry and invalidation being 2 different things).
If you want to more aggressively get rid of sessions, you can either invalidate them yourself in code, or you can configure a smaller scavenge interval. To do that, enable the "sessions" module (java -jar $jetty.home/start.jar --add-to-start=sessions) and configure the jetty.sessionScavengeInterval.seconds property. I don't think we've documented that adequately, so I've opened an issue to improve the doco on this aspect:
https://github.com/eclipse/jetty.project/issues/1793
cheers
Jan