9

是否可以从 gc 的角度将 java 对象标记为不可收集以节省 gc 扫描时间?

类似于http://wwwasd.web.cern.ch/wwwasd/lhc++/Objectivity/V5.2/Java/guide/jgdStorage.fm.html的东西,特别是那里的非垃圾收集容器(非垃圾-可收藏?)。

问题是我有很多普通的临时对象,但我有更大(几个 Gigs)的对象存储用于缓存目的。Java GC 不应该无缘无故地遍历所有那些试图找到任何要收集的缓存千兆字节,因为它们包含有自己超时的缓存数据。

通过这种方式,我可以以自定义方式将我的数据划分为无限生命和正常生命的对象,并且希望 GC 会非常快,因为普通对象的生命周期不会那么长并且数量较少。

这个问题有一些解决方法,例如Apache DirectMemory和 Commercial Terracotta BigMemory(http://terracotta.org/products/bigmemory),但是 java-native 解决方案会更好(我的意思是免费并且可能更可靠?)。另外我想避免序列化开销,这意味着它应该发生在同一个 jvm 中。据我了解,DirectMemory 和 BigMemory 主要在堆外运行,这意味着对象必须在 jvm 外部的内存中序列化/反序列化。简单地在 jvm 中标记非 gc 区域似乎是一个更好的解决方案。使用文件进行缓存也不是一种选择,它具有相同的无法承受的序列化/反序列化开销 - 用例是一个 HA 服务器,其中大量数据以随机(人工)顺序使用并且需要低延迟。

4

5 回答 5

2

JVM 管理的任何内存也由 JVM 进行垃圾收集。任何无需反序列化即可直接用于 Java 方法的“活动”对象都必须存在于 JVM 内存中。因此,据我了解,您不能拥有不受垃圾收集影响的活动对象。

另一方面,您描述的用法应该使垃圾收集的分代方法非常有效。如果您的大物件在附近停留了一段时间,它们会被检查回收的频率降低。所以我怀疑避免这些检查会带来很多好处。

于 2012-07-04T16:34:09.520 回答
1

是否可以从 gc 的角度将 java 对象标记为不可收集以节省 gc 扫描时间?

不,这是不可能的。

您可以通过保持对象可访问性来防止对象被垃圾收集,但 GC 仍需要跟踪它们以检查每个完整对象的可访问性;GC(至少)。

只是我的假设,当 jvm 饿死时,它也开始扫描所有那些不必要的对象。

是的。那是对的。但是,除非您有很多想要以这种方式处理的对象,否则开销可能微不足道。(无论如何,一个更好的主意是给 JVM 更多内存……如果可能的话。)

于 2012-07-04T16:52:44.433 回答
0

很简单,为了能够做到这一点,垃圾收集算法需要知道这样的标志,并在工作时考虑到它。

我不知道有任何标准 GC 算法具有这样的标志,因此要使其工作,您需要编写自己的 GC 算法(在确定一些可行的方式来传达此信息之后)。

原则上,事实上,您已经开始走这条路——您正在决定应该如何进行垃圾收集,而不是乐于将其留给 JVM 的 GC 算法。你描述的情况是你可以衡量的问题吗?现有垃圾收集不足但您的计划可行的东西?垃圾收集器经过了非常好的调整,所以如果“低效”的默认策略实际上比您天真的最优策略更快,我不会感到惊讶。

(在最好的情况下,进行手动内存管理很棘手且容易出错;在使用库存垃圾收集器处理其余部分时自己管理一些内存似乎更糟。我希望您会遇到很多边缘情况,其中 GC假设它“知道”整个堆发生了什么,这将不再是真的。如果可以的话,请避开......)

于 2012-07-04T16:58:45.653 回答
0

推荐的方法是使用商业 RTSJ 实现来避免 GC,或者使用堆外内存。也可以查看缓存的软引用(它们确实被收集)。

不建议这样做:如果由于某种原因您认为这些选项不够用,您可以查看不安全的直接内存访问(sun.misc.Unsafe 的一部分)。您可以使用“theUnsafe”字段来获取“Unsafe”实例。Unsafe 允许通过 'allocateMemory' 和 'freeMemory' 分配/释放内存。这不受 GC 控制,也不受 JVM 堆大小的限制。一旦你走这条路,就不能保证对 GC/应用程序的影响——这就是为什么使用字节缓冲区可能是要走的路(如果你不使用类似 RTSJ 的实现)。

希望这可以帮助。

于 2012-07-04T17:23:28.217 回答
0

活着的 Java 对象将始终是 GC 生命周期的一部分。或者换一种说法,将对象标记为非 gc 与让对象被根引用(例如静态最终映射)引用的开销顺序相同。

但再想一想,放入缓存中的数据很可能是临时的,最终会被驱逐。那时你会重新开始喜欢 JVM 和 GC。如果您有 100 GB 的永久数据,您可能需要重新考虑应用程序的架构,并尝试对数据进行分片和分发(水平可扩展性)。

最后但并非最不重要的一点是,围绕序列化做了很多工作,序列化的开销(我不是在谈论 ObjectInputStream 和 ObjectOutputStream 的不良声誉)并没有那么大。不仅如此,如果您的数据主要由原始类型(包括字节数组)组成,那么有一种有效的方法可以从堆外缓冲区(例如netty.io 的 ChannelBuffer )中读取 readInt() 或 readBytes( ) 。这可能是一条路。

于 2012-07-04T19:30:19.243 回答