11、关于对象池的思考

对象池作为全局资源,高并发环境中多个线程可能同时需要获取对象池的对象,因此多个线程在争抢对象时因为锁竞争而阻塞,因此使用对象池有线程同步的开销,而不使用对象池则有创建和销毁对象的开销。对于对象池本身的设计来说,需要尽量做到无锁化,比如Jetty就使用了ConcurrentLinkedDeque。如果你的内存足够大,可以考虑线程本地对象池,这样每个线程都有自己的对象池,线程之间互不干扰。

为了防止对象池的无限膨胀,必须要对池的大小做限制。对象池太小发挥不了作用,对象池太大的话可能有空闲对象,这些空闲对象会一直占用内存,造成内存浪费。这里你需要根据实际情况做一个平衡,因此对象池本身除了应该有自动扩容的功能,还需要考虑自动缩容。

所有的池化技术,包括缓存,都会面临内存泄露的问题,原因是对象池或者缓存的本质是一个Java集合类,比如List和Stack,这个集合类持有缓存对象的引用,只要集合类不被GC,缓存对象也不会被GC。维持大量的对象也比较占用内存空间,所以必要时我们需要主动清理这些对象。以Java的线程ThreadPoolExecutor
为例,它提供了allowCoreThreadTimeOut和setKeepAliveTime两种方法,可以在超时后销毁线程,我们在实际项目中也可以参考这个策略。

另外在使用对象池时,我这里还有一些小贴士供你参考:

  • 对象在用完后,需要调用对象池的方法将对象归还给对象池。
  • 对象池中的对象在再次使用时需要重置,否则会产生脏对象,脏对象可能持有上次使用的引用,导致内存泄漏等问题,并且如果脏对象下一次使用时没有被清理,程序在运行过程中会发生意想不到的问题。
  • 对象一旦归还给对象池,使用者就不能对它做任何操作了。
  • 向对象池请求对象时有可能出现的阻塞、异常或者返回null值,这些都需要我们做一些额外的处理,来确保程序的正常运行。

你可能感兴趣的:(11、关于对象池的思考)