16

我知道这可能是一个愚蠢的问题,但我的背景更多的是 C++ 和管理我自己的内存。

我目前正在减少我可以从我的一个游戏中获得的每一个分配,以尝试减少垃圾收集的频率和感知的“滞后”,所以对于我创建的每个变量都是一个对象(例如字符串和矩形)我确保我事先在构造函数中创建它,而不是在简单的 10 行函数中创建临时变量......(我希望这是有道理的)

无论如何,尽管今晚我工作了一些,但我意识到我对垃圾收集和原始类型(int、boolean、float)的假设可能完全错误,这些原始类型变量是我在一个被调用的 10 行函数中创建的每秒 20 次增加了我的垃圾收集问题?

所以一年前每隔几秒钟我就会在 logcat 中看到一条消息,比如

GC 在 101 毫秒内释放了 4010 个对象 / 484064 个字节

现在我每隔 15-90 秒左右就会看到一条消息……

因此,重新表述我的问题:看到此消息时是否包含原始类型(int、float、boolean 等)?

4

3 回答 3

25

原始类型不是对象,因此它们不会导致任何垃圾回收。但是,您必须非常小心,因为由于装箱,原始类型很容易成为对象,而无需您明确这样做。

例如,如果你想要一个整数键的 HashMap<>,你会使用 HashMap。请注意,因为“int”不是对象,所以不能在容器中使用。Integer 是原始 int 的对象版本。当您编写这样的代码时,将自动为您创建一个 Integer 对象:

HashMap<Integer, Object> map = new HashMap<Integer, Object>();
int someNum = 12345;    // no object created.
map.put(someNum, null); // Integer object created.

请注意,如果您不使用泛型,则会发生完全相同的事情,但更隐蔽:

HashMap map = new HashMap();
int someNum = 12345;    // no object created.
map.put(someNum, null); // Integer object created.

对于这种特殊情况,您可以使用 Android 的 SparseArray 类,它是原始整数键的容器。

于 2010-03-19T05:14:41.913 回答
6

似乎答案是否定的。看起来原语在 Java 中被放在堆栈上而不是在堆中,并且只有对象被垃圾收集。我发现了很多关于这个的简短参考,请查看 Wikipedia。对于一些稍微繁重的阅读,请参阅一篇关于 JVM 垃圾收集实现的论文,该论文更明确地解释了原语存储在物理上独立的内存位置,因此它们不会被错误地包含在垃圾收集。如果您想略读,第 4 页是最直接解释这一点的地方。

这是 android 特定线程,说明 gc仅扫描指针以及它如何检查

于 2010-03-19T03:04:01.073 回答
2

[注意:我还没有完整的评论权限,所以我将其添加为单独的答案。]

看起来原语在 Java 中被放在堆栈上而不是在堆中,并且只有对象被垃圾收集。

这不太准确。基元可以保存在局部变量中,也可以作为类的静态或实例字段。在后一种情况下,它们确实存储在堆上,但重要的是它们没有自己的“生命”,特别是没有与它们所包含的对象分开 gc'ed。

Android 不运行标准的基于堆栈的 JVM,它有自己的基于寄存器的 VM。

这是一个真实的陈述,但它也有点误导,并且与原始问题的重点无关。事实上,当一个方法调用另一个(等等)时,这些方法的激活帧存储在 Dalvik 实现中的堆栈中。不同之处在于,当孤立地查看激活帧时,Dalvik 激活帧中不包含可变大小的堆栈。在这方面,Dalvik 安排激活帧的方式更像是传统的类 C 语言(例如 C 或 C++)的处理方式。

于 2011-05-05T23:43:15.293 回答