I think the answers are right but I think something is missing.
The thing which is missing is "why and what it solves ?".
Ok let's start.
First let's mention some info:
All modules has access to the root services.
So even lazy loaded modules can use a service which was provided in app.module.
What will happen if a lazy loaded module will provide to itself a service which the app module already provided ? there will be 2 instances.
It's not a problem but sometimes it is.
How can we solve it ? simply don't import a module with that provider to lazy loaded modules.
End of story.
This ^ was just to show that lazy loaded modules has their own injection point ( as opposed to non-lazy-loaded modules).
But what happens when a shared(!) module has declared providers, and that module is imported by lazy and app.module? Again, like we said, two instances.
So how can we solve this in the shared module POV ? We need a way not to use providers:[] ! Why? because they will be auto imported to both consuming lazy and app.module and we don't want that as we saw that each will have a different instance.
Well, it turns out that we can declare a shared module that won't have providers:[], but still, will provide providers ( sorry :))
How? Like this :

Notice, no providers.
But
Entering Manual mechanism via convention :
You will notice that the providers in the pictures have service1 and service2
This allows us to import service2 for lazy loaded modules and service1 for non-lazy modules. ( cough...router....cough)
BTW, no one is stopping you to call forRoot within a lazy module. but you will have 2 instances because app.module should also do it - so don't do it in lazy modules.
Also - if app.module calls forRoot (and no one calls forchild) - that's fine, but root injector will only have service1. ( available to all app)
So why do we need it? I'd say :
It allows a shared module , to be able to split its
different-providers to be used with eager modules and lazy modules -
via forRoot and forChild convention. I repeat : convention
That's it.
WAIT !! not a single word about singleton ?? so why do I read
singleton everywhere ?
Well - it's hidden in the sentence above ^
It allows a shared module, to be able to split its
different-providers to be used with eager modules and lazy modules -
via forRoot and forChild.
The convention (!!!) allows it to be singleton - or to be more precise - if you won't follow the convention - you will NOT get a singleton.
So if you only load forRoot in the app.module , then you get only one instance because you only should call forRoot it in the app.module.
BTW - at this point you can forget about forChild. the lazy loaded module shouldn't / won't call forRoot - so you're safe in POV of singleton.
forRoot and forChild are not one unbreakable package - it's just that there is no point of calling for Root which obviously will be loaded only in app.module without giving the ability for lazy modules , have their own services , without creating new services-which-should-be-singleton.
This convention give you a nice ability called forChild - to consume "services only for lazy loaded modules".
Here is a demo Root providers yields positive numbers , lazy loaded modules yields negative numbers.
RouterServicefor a single Angular2 application.forRootwill initialize that service and register it to DI together with some route config, whileforChildwill only register additional route configs and tell Angular2 to reuse theRouterServicethatforRoothas created. - Harry Ninh