如何提高垃圾收集性能?

时间:2021-07-17 00:18:30

What kind of optimization patterns can be used to improve the performance of the garbage collector?

可以使用哪种优化模式来提高垃圾收集器的性能?

My reason for asking is that I do a lot of embedded software using the Compact Framework. On slow devices the garbage collection can become a problem, and I would like to reduce the times the garbage collector kicks in, and when it does, I want it to finish quicker. I can also see that working with the garbage collector instead of against it could help improve any .NET or Java application, especially heavy duty web applications.

我的理由是我使用Compact Framework做了很多嵌入式软件。在慢速设备上,垃圾收集可能会成为一个问题,我想减少垃圾收集器启动的次数,当它发生时,我希望它能更快地完成。我还可以看到使用垃圾收集器而不是反对它可以帮助改进任何.NET或Java应用程序,尤其是重型Web应用程序。

Here are some of my thoughts, but I haven’t done any benchmarks.

以下是我的一些想法,但我没有做任何基准测试。

  • reusing temporary classes/arrays (keep down allocation count)
  • 重用临时类/数组(保持分配计数)

  • keeping the amount of live objects to a minimum (faster collections)
  • 将活动对象的数量保持在最低(更快的收集)

  • try to use structs instead of classes
  • 尝试使用结构而不是类

6 个解决方案

#1


12  

The key is to understand how the CF GC works for allocations. It's a simple mark-and-sweep, non-generational GC with specific algorithms for what will trigger a GC, and what will cause compaction and/or pitching after collection. There is almost nothing you can do at an app level to control the GC (the only method available is Collect, and it's use is pretty limited, as you can't force compaction anyway).

关键是要了解CF GC如何用于分配。它是一个简单的标记 - 扫描,非代际GC,具有触发GC的特定算法,以及在收集后会导致压缩和/或投球的因素。在应用程序级别几乎没有什么可以控制GC(唯一可用的方法是Collect,它的使用非常有限,因为你无论如何都不能强制压缩)。

Object re-use is a good start, but simply keeping the object count low is probably one of the best tools, as all roots have to be walked for any collection operation. Keeping that walk short is a good idea. If compaction is killing you, then preventing segment fragmentation will help. Objects >64k can be helpful in that regard as they get their own segment and are treated differently than smaller objects.

对象重用是一个良好的开端,但简单地保持对象数量较低可能是最好的工具之一,因为所有根都必须遍历任何集合操作。保持走路是一个好主意。如果压缩会杀死你,那么阻止段碎片会有所帮助。对象> 64k在这方面可能会有所帮助,因为它们获得自己的段并且与较小的对象区别对待。

To really understand how the CF GC works, I'd recommend watching the MSDN Webcast on CF memory management.

要真正了解CF GC的工作原理,我建议您观看有关CF内存管理的MSDN网络广播。

#2


3  

The single most important aspect is to minimize the allocation rate. Whenever an object is allocated, it needs GC later. Now of course, if the object is small or shortlived it will get nailed in the young generation (provided that the GC is generational). Large objects tend to go directly into the tenured arena. But avoiding having to collect at all is even better.

最重要的一个方面是最小化分配率。每当分配一个对象时,它就需要GC。当然,如果对象很小或是短暂的,它将在年轻一代中被钉(假设GC是世代的)。大型物体往往直接进入终身体育馆。但是避免必须收集甚至更好。

Also, if you can throw things on the stack, you will enjoy much less pressure on the GC. You could attempt toying with GC-options, but I think you would be much better helped with an allocation profiler in hand, so you can find the spots that makes the problems.

此外,如果你可以把东西放在堆叠上,你将在GC上获得更少的压力。您可以尝试使用GC选项进行操作,但我认为您可以更好地使用分配探查器,因此您可以找到导致问题的位置。

The thing one should beware is the weight of standard libraries and frameworks. You wrap a couple of objects and it will fill up pretty quickly. Remember, whenever something goes on the GC-heap, it usually uses a bit more space for GC-bookkeeping. So your 1000 pointers allocated individually is much bigger than an array/vector of the same pointers since the latter can share the GC-bookkeeping. On the other hand, the latter will probably stay alive for much longer.

应该注意的是标准库和框架的重量。你包装几个对象,它会很快填满。请记住,每当GC堆上发生某些事情时,它通常会使用更多空间进行GC簿记。因此,单独分配的1000个指针比相同指针的数组/向量大得多,因为后者可以共享GC簿记。另一方面,后者可能会活得更长久。

#3


2  

The struct vs class issue is a complex one... you might easily end up using a lot more stack space, for example. And you certainly don't want mutable structs. But the other points seem sensible, as long as you aren't bending the design out of shape to accomodate it.

结构与类的问题是一个复杂的问题......例如,您最终可能很容易使用更多的堆栈空间。你肯定不想要可变的结构。但其他点似乎是明智的,只要你不弯曲设计变形以适应它。

[edit] One other common gotcha is string concatenation; if you are doing concatenation in a loop, use StringBuilder, which will remove a lot of intermediate strings. It might be that GC is busy collecting all the abandoned vesions of your strings?

[编辑]另一个常见的问题是字符串连接;如果你在循环中进行连接,请使用StringBuilder,这将删除许多中间字符串。 GC可能正在忙着收集所有被遗弃的字符串?

#4


2  

Another option would be to manually collect the garbage during non-peak times in your application using GC.Collect() (assuming this is available in CF). This could reduce the objects required for cleanup later in your application.

另一种选择是使用GC.Collect()在应用程序的非高峰时间手动收集垃圾(假设这在CF中可用)。这可以减少应用程序中稍后清理所需的对象。

#5


2  

One important fact is to keep the lifetime of your objects as short as possible.

一个重要的事实是尽可能缩短对象的生命周期。

#6


0  

I heard a .NET Rocks show on Rotor 2.0. If you are really hardcore, you could download Rotor, tweak the source, and use your own modified garbage collector.

我在Rotor 2.0上听到了一个.NET Rocks节目。如果你真的是硬核,你可以下载Rotor,调整源代码,并使用你自己的修改过的垃圾收集器。

In any case, that podcast has some great info on the GC. I highly recommend listening to it.

无论如何,该播客在GC上有一些很棒的信息。我强烈建议听一听。

#1


12  

The key is to understand how the CF GC works for allocations. It's a simple mark-and-sweep, non-generational GC with specific algorithms for what will trigger a GC, and what will cause compaction and/or pitching after collection. There is almost nothing you can do at an app level to control the GC (the only method available is Collect, and it's use is pretty limited, as you can't force compaction anyway).

关键是要了解CF GC如何用于分配。它是一个简单的标记 - 扫描,非代际GC,具有触发GC的特定算法,以及在收集后会导致压缩和/或投球的因素。在应用程序级别几乎没有什么可以控制GC(唯一可用的方法是Collect,它的使用非常有限,因为你无论如何都不能强制压缩)。

Object re-use is a good start, but simply keeping the object count low is probably one of the best tools, as all roots have to be walked for any collection operation. Keeping that walk short is a good idea. If compaction is killing you, then preventing segment fragmentation will help. Objects >64k can be helpful in that regard as they get their own segment and are treated differently than smaller objects.

对象重用是一个良好的开端,但简单地保持对象数量较低可能是最好的工具之一,因为所有根都必须遍历任何集合操作。保持走路是一个好主意。如果压缩会杀死你,那么阻止段碎片会有所帮助。对象> 64k在这方面可能会有所帮助,因为它们获得自己的段并且与较小的对象区别对待。

To really understand how the CF GC works, I'd recommend watching the MSDN Webcast on CF memory management.

要真正了解CF GC的工作原理,我建议您观看有关CF内存管理的MSDN网络广播。

#2


3  

The single most important aspect is to minimize the allocation rate. Whenever an object is allocated, it needs GC later. Now of course, if the object is small or shortlived it will get nailed in the young generation (provided that the GC is generational). Large objects tend to go directly into the tenured arena. But avoiding having to collect at all is even better.

最重要的一个方面是最小化分配率。每当分配一个对象时,它就需要GC。当然,如果对象很小或是短暂的,它将在年轻一代中被钉(假设GC是世代的)。大型物体往往直接进入终身体育馆。但是避免必须收集甚至更好。

Also, if you can throw things on the stack, you will enjoy much less pressure on the GC. You could attempt toying with GC-options, but I think you would be much better helped with an allocation profiler in hand, so you can find the spots that makes the problems.

此外,如果你可以把东西放在堆叠上,你将在GC上获得更少的压力。您可以尝试使用GC选项进行操作,但我认为您可以更好地使用分配探查器,因此您可以找到导致问题的位置。

The thing one should beware is the weight of standard libraries and frameworks. You wrap a couple of objects and it will fill up pretty quickly. Remember, whenever something goes on the GC-heap, it usually uses a bit more space for GC-bookkeeping. So your 1000 pointers allocated individually is much bigger than an array/vector of the same pointers since the latter can share the GC-bookkeeping. On the other hand, the latter will probably stay alive for much longer.

应该注意的是标准库和框架的重量。你包装几个对象,它会很快填满。请记住,每当GC堆上发生某些事情时,它通常会使用更多空间进行GC簿记。因此,单独分配的1000个指针比相同指针的数组/向量大得多,因为后者可以共享GC簿记。另一方面,后者可能会活得更长久。

#3


2  

The struct vs class issue is a complex one... you might easily end up using a lot more stack space, for example. And you certainly don't want mutable structs. But the other points seem sensible, as long as you aren't bending the design out of shape to accomodate it.

结构与类的问题是一个复杂的问题......例如,您最终可能很容易使用更多的堆栈空间。你肯定不想要可变的结构。但其他点似乎是明智的,只要你不弯曲设计变形以适应它。

[edit] One other common gotcha is string concatenation; if you are doing concatenation in a loop, use StringBuilder, which will remove a lot of intermediate strings. It might be that GC is busy collecting all the abandoned vesions of your strings?

[编辑]另一个常见的问题是字符串连接;如果你在循环中进行连接,请使用StringBuilder,这将删除许多中间字符串。 GC可能正在忙着收集所有被遗弃的字符串?

#4


2  

Another option would be to manually collect the garbage during non-peak times in your application using GC.Collect() (assuming this is available in CF). This could reduce the objects required for cleanup later in your application.

另一种选择是使用GC.Collect()在应用程序的非高峰时间手动收集垃圾(假设这在CF中可用)。这可以减少应用程序中稍后清理所需的对象。

#5


2  

One important fact is to keep the lifetime of your objects as short as possible.

一个重要的事实是尽可能缩短对象的生命周期。

#6


0  

I heard a .NET Rocks show on Rotor 2.0. If you are really hardcore, you could download Rotor, tweak the source, and use your own modified garbage collector.

我在Rotor 2.0上听到了一个.NET Rocks节目。如果你真的是硬核,你可以下载Rotor,调整源代码,并使用你自己的修改过的垃圾收集器。

In any case, that podcast has some great info on the GC. I highly recommend listening to it.

无论如何,该播客在GC上有一些很棒的信息。我强烈建议听一听。