void scheduleClientPingTask() {
    clientPingTask = new SystemTimer.SystemTimerTask() {

      @Override
      public void run2() {
        // If there are no proxies, return
        if (_clientProxies.isEmpty()) {
          return;
        }

        // Create ping message
        ClientMessage message = new ClientPingMessageImpl();

        // Determine clients to ping
        for (CacheClientProxy proxy : getClientProxies()) {
          logger.debug("Checking whether to ping {}", proxy);
          // Send the ping message directly to the client. Do not qo through
          // the queue. If the queue were used, the secondary connection would
          // not be pinged. Instead, pings would just build up in secondary
          // queue and never be sent. The counter is used to help scalability.
          // If normal messages are sent by the proxy, then the counter will
          // be reset and no pings will be sent.
          if (proxy.incrementAndGetPingCounter() >= CLIENT_PING_TASK_COUNTER) {
            logger.debug("Pinging {}", proxy);
            proxy.sendMessageDirectly(message);
            logger.debug("Done pinging {}", proxy);
          } else {
            logger.debug("Not pinging because not idle: {}", proxy);
          }
        }
      }
    };

    if (logger.isDebugEnabled()) {
      logger.debug("Scheduling client ping task with period={} ms", CLIENT_PING_TASK_PERIOD);
    }
    cache.getCCPTimer().scheduleAtFixedRate(clientPingTask,
        CLIENT_PING_TASK_PERIOD, CLIENT_PING_TASK_PERIOD);
  }
This starts the cleanup thread that periodically (DefaultQuery.TEST_COMPILED_QUERY_CLEAR_TIME)
checks for the compiled queries that are not used and removes them.
geode-core/src/main/java/org/apache/geode/internal/cache/tier/sockets/CacheClientNotifier.java