You can reduce memory usage and startup time by loading VIs and DLLs dynamically. Memory usage isn't a big deal these days unless you are on an embedded device but startup time is.
I recommend using DLLs only when calling C/C++ code from LV. There are safer and easier ways to call LV code from LV. Here are two:
1) (The old standby). Use the VI Server's "invoke node" or "call by reference" methods to call a VI that you specify by path. In more recent versions of LV you can get the path from a static VI reference, which both makes it easier to edit the called VI from the dev env and it hides all of the messiness with paths changing when you move from the dev env to a built application. You'll need to manually specify the VI when you build the application.
2) (New and improved) Starting in LV 8 or 8.5, NI added a "Call Setup" option to SubVI options. (Right-click on a VI, Call Setup...). Now you can set a VI to "load with callers" (the default), "reload for each call" or "load and retain on first call". I don't have much experience with this myself, but it looks like it will do exactly what you want, and it's less obscure than using the VI Server. (You'd still need the VI server to do plug-ins and things like that.) I assume the App Builder will include the VI in the app automatically if you do it this way.
For .NET assemblies, I'd call them using LV's .NET functionality rather than via the DLL interface, since any DLL exposes your app to runtime crashes from bad pointers. (Also LabView loads DLLs at startup by default, so unless you do tricks such as the above or defer definition of the DLL path until runtime, it won't make any difference in the memory footprint.)
-Rob