.Net vs Java垃圾收集器

27 浏览
0 Comments

.Net vs Java垃圾收集器

有人知道Java和.Net垃圾回收器之间的主要区别吗?网络搜索没有找到太多信息,这是一个在测试中出现的问题。

0
0 Comments

Java的垃圾回收器(Garbage Collector)与.NET的垃圾回收器在一些方面存在差异。其中一个显著的区别是,据我所知,JVM不会像CLR那样将释放的内存返回给操作系统。

这个差异导致了一些问题的出现。当JVM的垃圾回收器释放内存时,它不会将这些内存块返回给操作系统。相反,它会将这些内存重新分配给Java应用程序的堆。这意味着当应用程序需要更多内存时,JVM会尝试从这些已释放但未返回给操作系统的内存块中分配,而不是从操作系统申请新的内存。

这种行为可能导致内存使用效率低下。如果应用程序需要大量内存,但JVM无法从已释放的内存块中获取足够的内存,它将不得不频繁地向操作系统请求新的内存,这可能会导致应用程序的性能下降。

为了解决这个问题,可以尝试使用一些策略来优化内存使用。其中一种方法是调整JVM的堆大小,以确保可以容纳应用程序所需的内存。另一种方法是使用手动内存管理,即在不再需要使用的内存块上显式地调用释放内存的操作,这样可以确保这些内存块被及时返回给操作系统。

另外,使用Java的NIO(New I/O)库可以提供更高效的内存管理。NIO库提供了直接内存(Direct Memory)的概念,它可以绕过JVM的堆,将内存直接分配给操作系统。这样,当内存不再需要时,JVM无需参与其释放过程,可以直接返回给操作系统。

尽管存在一些差异和问题,Java的垃圾回收器仍然是一个强大的工具,可以自动管理内存,减轻开发人员的负担。通过了解这些差异和解决方法,我们可以更好地理解和优化Java应用程序的内存性能。

0
0 Comments

在.NET中,垃圾收集器(Garbage Collector)使用了一个被称为大对象堆(Large Object Heap,LOH)的概念。CLR会预先分配一批对象到LOH中,而且所有大小至少为85000字节的用户分配对象也会被分配到LOH中。此外,由于某种内部优化,长度为1000或更多的double[]数组也会被分配到LOH中。

LOH与其他代的堆的处理方式有一些不同:

- LOH只会在进行完整的垃圾收集(full collect)时进行清理,而且不像其他代的堆一样进行紧凑(compact)。

- 从LOH分配内存时使用的是类似C运行时中malloc的自由列表(free list)方式,而从其他代的堆分配内存只需要移动一下一级代(generation 0)的指针。

不清楚JVM是否有类似的机制,但这些对于了解.NET中的内存处理方式是非常重要的信息,希望对你有所帮助。

另外,现在.NET中有一个用于紧凑LOH的选项,默认是关闭的。这个选项被称为“compact once”,打开后,下一次阻塞的第二代(Gen2)垃圾收集将会紧凑LOH。一旦紧凑完成,该选项会恢复为关闭/默认状态。

以上就是.NET和Java垃圾收集器之间差异的一些原因和解决方法。

0
0 Comments

.Net vs Java垃圾收集器的区别主要在于CLR(.Net) GC和JVM GC,而不是语言本身。

这两者都可能发生变化,其行为规范宽松,允许进行更改而不影响程序的正确性。

由于.Net是根据Java(和其他基于GC的平台)的演变经验而设计的,所以存在一些历史上的差异。请不要假设.Net的垃圾收集器在某种程度上是优越的,因为它从一开始就包含了功能,这只是后来的结果。

一个明显的公开可见的差异是,MS GC通过GC API公开了其代际特性。这很可能在一段时间内保持不变,因为这是一种基于大多数程序表现的明显方法:大多数分配的生命周期极短。

最初的JVM没有代际垃圾收集器,尽管这个功能很快被添加进来。

最早由Sun和其他公司实现的代际收集器往往是标记和扫描的。人们意识到,采用标记-扫描-整理的方法可以更好地提高内存局部性,从而使附加的复制开销得到合理的解释。CLR runtime首次使用了这种行为。

Sun和Microsoft的GC实现方法之间的区别在于可配置性。

Sun提供了大量的选项(在命令行中)来调整GC的各个方面或在不同模式之间切换。许多选项以-X或-XX开头,表示它们在不同版本或供应商之间不受支持。相比之下,CLR几乎没有可配置性;你唯一的选择是使用服务器或客户端收集器,它们分别针对吞吐量和延迟进行了优化。

两家公司(以及开源实现)都在进行GC策略的积极研究。最新的GC实现中使用的当前方法包括线程本地的伊甸区(提高局部性,使伊甸集合可能不会引起完全暂停)以及尽早分配的方法,试图避免将某些分配放入伊甸代。

值得注意的是,正常的.Net使用一种称为"写障碍(write fence)"的技术来跟踪自上次晋升以来已写入的对象。据我所了解,如果一个对象从gen0晋升到gen1,并且自那时以来没有被写入,那么它不可能包含对gen0对象的任何引用;在gen0收集期间,没有必要检查其中包含的任何引用,因为它只引用将被认为是"活动"的对象。在gen0收集期间跳过未写入的gen1/gen2对象是一个巨大的优势。

我相信大多数(所有?)Java的代际GC都使用了相同的技术,说实话,我看不出Java有什么特性会排除使用它。

"人们意识到,采用标记-扫描-整理的方法可以更好地提高内存局部性,从而使附加的复制开销得到合理的解释。" - 我相当确定Java设计者在Java 1.0发布之前就已经充分了解这一点。问题是,由于市场要求,Java 1.0必须在实现代际GC(以及JIT编译器和许多其他功能)之前发布。

对于.Net的GC,值得注意的是,支持额外配置选项的新版本正在不断增加,这些选项在原始答案中没有体现。请参见learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector。

文章整理自Stack Overflow的回答。

0