通过Goetz“Java Concurrency in Practice”一书,他提出了反对使用对象池(第11.4.7节)的案例 - 主要论点:
1)Java中的分配比C的malloc快 2)从池中请求对象的线程需要昂贵的同步
我的问题不在于分配速度慢,而是定期垃圾收集会在响应时间中引入异常值,可以通过减少对象池来消除。
在使用这种方法时,我没有看到任何问题吗?基本上我是跨线程分区对象池...
答案 0 :(得分:2)
如果它的线程是本地的那么你可以忘掉这个:
2)从池中请求对象的线程需要昂贵的同步
作为线程本地,您不必担心从池本身检索同步。
答案 1 :(得分:2)
在Java 1.4中,对象分配相对昂贵,因此即使是简单对象的对象池也可以提供帮助。但是,在Java 5.0中,对象分配得到了显着改善,但是同步仍有一段路要走,这意味着对象分配比同步更快。即,在许多情况下移除对象池改进了性能。在Java 6中,同步已经改进到对象池在简单情况下可以对性能产生一点差异的程度。
避免使用简单的对象池是一个好主意,因为它更简单,不是出于性能原因。
对于更复杂/更大的对象,即使您使用同步,对象池在Java 6中也很有用。例如套接字,文件流或数据库连接。
答案 2 :(得分:2)
(sun's)GC扫描实时对象。假设在典型的Java程序运行时中存在比活动对象更多的死对象。它标记了活的物体,并处理剩下的物体。
如果你缓存了很多对象,它们都是实时的。如果你有几GB这样的物体,GC会浪费很多时间徒劳地扫描它们。长时间的GC停顿会使您的应用程序瘫痪。
缓存一些东西只是为了让它非垃圾无法帮助GC。
这并不是说缓存是错误的。如果您有15G内存,而您的数据库是10G,为什么不将所有内容缓存在内存中,因此响应速度很快。请注意,这是缓存一些本来很慢的东西。
为了防止GC无效地扫描10G缓存,缓存必须在GC的控制之外。例如,使用“memcached”,它存在于另一个进程中,并拥有自己的缓存优化GC。
最新消息是Terracotta的BigMemory,这是一个纯粹的java解决方案,可以做类似的事情。
线程本地池的一个例子是sun的直接ByteBuffer池。当我们打电话
channel.read(byteBuffer)
如果byteBuffer不是“直接”,则必须在引擎盖下分配“直接”,用于与OS通信数据。在网络应用程序中,这样的分配可能非常频繁,丢弃刚刚分配的分配似乎是浪费,并在下一个语句中立即分配另一个分配。 sun的工程师,显然不信任GC那么多,创建了一个线程本地池“直接”ByteBuffers。
答案 3 :(得分:0)
我认为您的案例是使用合并的合理情况。合并没有邪恶,Goetz意味着你不应该在没有必要时使用它。另一个例子是连接池,因为创建连接非常昂贵。
答案 4 :(得分:0)
如果是threadlocal,很可能你甚至不需要汇集。当然这取决于用例,但是在给定的线程上你可能只需要在给定时间内只有一个这种类型的对象。
然而,使用threadlocals的警告是内存管理。请注意,在拥有这些threadlocals的线程消失之前,threadlocal值不会轻易消失。因此,如果你有大量的线程和大量的threadlocals,它们可能会对使用过的内存产生很大影响。
答案 5 :(得分:0)
我肯定会尝试一下。虽然现在是“常识”,人们不应该关心对象创建,但实际上使用对象池和特定类可能会获得很多性能。对于文件处理框架,我从pooling object []对象中获得了5%的读取性能。
所以试试看你的执行时间,看看你是否获得了任何收益。
答案 6 :(得分:0)
即使这是一个老问题,2个线程请求池中的对象需要代价高昂的同步并不完全适用。
可以编写一个并发(无同步)对象池,它甚至不会在快速路径上展示共享(甚至虚假共享)。当然,在简单的情况下,每个线程可能都有自己的池(更像是一个关联的对象),但是这种贪婪的方法会导致资源浪费(如果资源无法分配则会导致饥饿/错误)
Pools适用于像ByteBuffers这样的重物,特别是。直接的,连接,套接字,线程等。总体上任何需要非Java干预的对象。
答案 7 :(得分:-2)
静态工厂的第二个优势 方法是,与构造函数不同, 他们不需要创建新的 每次调用它们的对象。这个 允许不可变类使用 预构建的实例或缓存 实例,因为它们的构造和 反复分配这些情况 以免造成不必要的 重复的对象。该 Boolean.valueOf(boolean)方法 说明了这种技术:从来没有 创建一个对象。这种技术可以 如果,大大提高性能 请求等效对象 经常,特别是如果这些 对象的创建成本很高。
我想说这取决于。如果它值得,那就去做吧。