Don't know if this is of any relevance for you...
What others said, but: at least if you are using Unity as your game engine, or your engine/framework has a similar weakness with instantiating/destroying gameobjects/whatever-it-is-called-in-your-framework, OR your own "engine" you write for your game there will end up having similar weaknesses, never EVER instantiate/destroy while running the game!
Always instatiate at scene load, manage as a pool during the game, and destroy at the end of the scene.
Why? Would you believe me that at least in case of Unity, the engine was able to handle hundreds and thousands of gameobjects with a renderer and physics (not all of them had their physics components active at the same time though) on a pretty up to date intel CPU and a high midrange GPU while keeping >30FPS as long as I was using a pool to "re use" the objects, but dropped to around 20 or less FPS while I was still using instantiate/destroy instead of managing the gameobjects with the pool?
Just so my usecase gets a little bit clearer, I was creating an automatic weapon at the time, that shot bullets with a renderer and physics rigidbody attached, and ejecting spent cartridges, again with a renderer and physics rigidbody attached. Madness? So I thought too, until I actually profiled the slow running scene and found the problem was neither rendering nor physics, but the obscene overhead of instantiate/destroy of a gameobject. Amplified by about 30-60 of both going on per second. Seems Unity and a midrange PC can pretty much handle the use case, as long as you optimize a little bit (for example use object pools).
Now, this might totally not affect you. But then, using a pool instead of instantiate/destroy doesn't cost much more time to implement, and will safeguard against such scenarios where instantiating and destroying an "object" (in case of unity, a gameobject comes with a multitude of components, its more like a whole mess of objects) costs more performance than anything else.
Just instantiate at startup (making a good assumption on how many bullets you are gonna need on the screen at any time), keep in a managed pool (reuse bullets that left the screen or impacted with something, move them back to the gun and reset their trajectory... if you need more bullets, instantiate more and add them to the pool, but never destroy them yet), and destroy them when the level is over (and you have a second or so anyway to dispose the rest of the level, and do not need to render anything on screen with a stable framerate).