When an application connects to a database it takes resources to establish that connection. So rather than doing this over and over again a connection pool is established to handle this functionality and cache connections. There are several issues that can arise if either the pool is not created with the same connection string (fragmentation), or if the connections are simply not closed/disposed of properly.

In the case of fragmentation, each connection string associated with a connection is considered part of 1 connection pool. If you create 2 connection strings with different database names, maxpool values, timeouts, or security then you will in effect create different connection pools. This is much like how query plans get stored in the plan cache. Different white space, capital letters all create different plans.

You can get the .NET pool counts from:

Performance Monitor> .NET data provider for SQL Server > NumberOfActiveConnectionPools

Another problem occurs if connections aren't closed or disposed of properly. These connections can exceed the default 100 connection pool limit and give you a nice application error. Or they can hold onto database resources and kill you slowly, like my daily commute. It could be as simple as as developer using a try/catch block without the finally to close the connection. Relying on the try section only to close the connection could be skipped by an exception.

You can read about connection pooling HERE and HERE