The gradients are calculated during the loss.backward() call.
You can try to print the gradients before and after this step using print(net.fc1.weight.grad).
Before the backward pass the gradients will be empty, after it you will see some values.
The optimizer just “knows” how to update the provided parameters using this gradient.
You can find more information in this beginner tutorial 46.