Miners (or mining pools) receive as their block reward, the sum of the block subsidy (currently 25 BTC) AND the total transaction fees paid by all transactions included in the block.
They do this by creating a special transaction that has no inputs, and assigns outputs that do not exceed this sum to any addresses (or scripts) that they like.
Notice, that I said they assign outputs that
do not exceed this sum.
In other words, the protocol is perfectly happy to let a miner assign
LESS than the reward that they are due in this transaction.
This has happened in the past accidentally. It is not likely to happen intentionally, but that doesn't mean it can't.
If they assign between 0.00000001 BTC and 25 BTC less than they are due, then you could just say they are accepting the fees, and reducing the number of new bitcoins created.
If the reward they assign in the transaction falls short of what they are due by more than 25 BTC, then clearly, some of the fees paid have vanished from existence.
The fees were paid by transactions, but they don't show up in any outputs, and no longer exist to be spent or to find in the list of unspent transaction outputs (UTXO).
Also note that transactions can be created that don't pay to an address, but rather pay to a script. In particular, you can use OP_RETURN to create an output that can never be spent. This script OP code is used specifically to mark an output as "invalid". As such, it can immediately be removed from the UTXO, and can be considered "destroyed".
https://en.bitcoin.it/wiki/ScriptProvably Unspendable/Prunable OutputsThe standard way to mark a transaction as provably unspendable is with a scriptPubKey of the following form:
scriptPubKey: OP_RETURN {zero or more ops}
OP_RETURN immediately marks the script as invalid, guaranteeing that no scriptSig exists that could possibly spend that output. Thus the output can be immediately pruned from the UTXO set even if it has not been spent.