Full node sync only preserve the last 128 history states

While researching on the different sync modes and their behaviors, I learned that geth full sync will replay all transactions from the genesis and preserve all history states, while fast sync will only download block data without statedb until around 100 blocks before the latest block head, and then perform as a full node. 
I decided to verify these behaviors in this way:  

I started a private network, and mined 2000 blocks with no transactions (but the balance of the miner should keep increasing because of the coinbase transactions).   
Then I started other geth instances with different dirs and ports. Connected them to the private network node in full and fast sync. 

Here's what I discovered:  

In full sync geth instance, I'm able to query the state(by calling eth.getBalance(eth.accounts[0], <blockNumber>)) only for the last 128 blocks. 
In fast sync geth instance, I'm able to query the state only for the last 64 blocks. 

In both cases, querying states from older blocks result in a missing trie node error. Digging deeper into the code, I found that those trie nodes (state roots of old blocks) are simply not saved in the underlying leveldb.
The behaviors did not match what I expected. I'm wondering if there's anything wrong in my experiment setting. Are there default state trie pruning that I'm not aware of? I'm using the latest geth 1.8. Any pointers are welcomed. 

The behavior has changed with the release of geth v1.8.0
Fromt the release notes

Tracing and pruning: By default, state for the last 128 blocks kept in memory. Most states are garbage collected. If you are running a block explorer or other service relying on transaction tracing without an archive node (--gcmode=archive), you need to trace within this window! Alternatively, specify the "reexec" tracer option to allow regenerating historical state; and ideally switch to chain tracing which amortizes overhead across all traced blocks.