There is a key difference between what you did and the workflow you described. In your example, you are backproping through the same graph x->3x->3x*x twice. Yet in usual workflow, each iteration a new graph is built and you backprop  through it only once.
If retain_graph is not specified, then graph will be freed after something backproped through it.