code: private void takeSnapshot() {
      state.checkThread();
  
      // If no snapshot has been taken, take a snapshot and hold it in memory until the complete
      // index has met the snapshot index. Note that this will be executed in the state machine thread.
      // Snapshots are only taken of the state machine when the log becomes compactable. If the log compactor's
      // compactIndex is greater than the last snapshot index and the lastApplied index is greater than the
      // last snapshot index, take the snapshot.
      Snapshot currentSnapshot = state.getSnapshotStore().currentSnapshot();
      if (pendingSnapshot == null && stateMachine instanceof Snapshottable
        && (currentSnapshot == null || (log.compactor().compactIndex() > currentSnapshot.index() && lastApplied > currentSnapshot.index()))) {
        pendingSnapshot = state.getSnapshotStore().createSnapshot(lastApplied);
  
        // Write the snapshot data. Note that we don't complete the snapshot here since the completion
        // of a snapshot is predicated on session events being received by clients up to the snapshot index.
        LOGGER.info("{} - Taking snapshot {}", state.getCluster().member().address(), pendingSnapshot.index());
        executor.executor().execute(() -> {
          synchronized (pendingSnapshot) {
            try (SnapshotWriter writer = pendingSnapshot.writer()) {
              ((Snapshottable) stateMachine).snapshot(writer);
            }
          }
        });
      }
    }
docs: Takes a snapshot of the state machine state if necessary.
  <p>
  Snapshots of the state machine are taken only once the log becomes compactable. This means snapshots
  are largely dependent on the storage configuration and ensures that snapshots are not taken more
  frequently than will benefit log compaction.