.NET:GC

.Net程序可以找出某个时间点上哪些已分配的内存空间没有被程序使用,并自动释放它们。自动找出并释放不再使用的内存空间的机制,就称为垃圾回收机制(Garbage Collection,简称GC)。

GC的主要工作是找出堆空间分配的空间中哪些空间不再被程序使用,然后回收这些空间。

在.Net中使用的方式是最主流的“标记并清除”的方式。这种方式会选择一部分引用类型的对象作为根对象然后递归标记对象与对象的引用类型成员。最后所有已标记的引用类型的对象会存活下来。而未被标记的引用类型的对象会被回收。

根对象的选择必须保证递归从根对象开始能够找到程序中所有能够访问的对象。.Net中的根对象,包括各个线程栈空间上的变量、全局变量、GC句柄和析构队列中的对象。 

GC中的机制


分代

.Net中将引用类型的对象分为三类:第0代、第1代、第2代

新分配的对象如果不超过一定的大小,就会作为小对象归属到第0代。如果超过一定大小就会作为大对象归属到第2代。执行垃圾回收之后,存活的第0代,通常情况下会变为第1代。存活的第1代则会变为第2代。存活的第2代仍然在第2代。正是这样的处理才使的.Net中的对象按照存活时间分为了三个群组。这样做的目的是尽量增加每次执行垃圾回收处理时,可回收的对象的数量,并减少处理所需的时间。

压缩

反复执行分配与回收操作,可能导致堆上产生很多空余空间,这些空余空间又被称为碎片空间。

压缩机制可以通过移动已分配空间把碎片空间合并到一块,使得堆可以分配更大的对象。

但是实现压缩机制是有难度的,其中最大的难点就是如何解决已分配空间地址改变的问题,因为空间地址改变以后所以指向这个空间的内存地址也需要响应的改变。也就是说它需要相应的修改栈空间和堆空间上引用类型的本地变量和成员。 

.Net运行时提供的GC是支持压缩机制的,但是这个压缩机制不是一定启用的,它只在某些情况下、在一定的条件下才会启用。

大小对象

.Net根据引用类型的对象值占用的空间大小来区分它是小对象还是大对象,大于或等于85000字节的为大对象,小于的为小对象。

 大对象和小对象会在不同的堆区域中分配:大对象堆和小对象堆。

移动大对象需要的成本很高,压缩对象默认只在小对象堆启用,大对象堆是不会执行压缩的。

固定对象

.Net支持托管代码调用非托管代码。如果我们把引用类型的对象传递给一个非托管代码,那么就会把内存地址复制到非托管代码管理的内存区域中,此时.Net运行时就无法得知非托管代码将内存地址保存到了哪里。这个时候就会产生两个问题:

  • 无法确定非托管代码是否还在使用这个对象。
  • 如果这个时候执行压缩操作,已分配的内存地址将会改变,此时非托管代码中保存的内存地址就不能同步更新。

为了解决上面的问题.Net就要求托管代码传递引用类型对象给非托管代码时,必须创建固定类型的GC句柄,并在托管代码中保持这个句柄存活到非托管代码的调用结束。这种创建了固定类型GC句柄的对象就称为固定对象。

析构队列

.Net支持在回收对象前调用析构函数。在析构函数中我们可以使用托管代码编写自定义的逻辑。这样对于运行时来说,它无法确定我们析构函数中执行什么操作,需要多少时间。此时就会产生一个问题:如果在垃圾回收的过程中执行这些析构函数,垃圾回收需要的时间是不可预料的。 

为了解决上面的问题.Net内部定义了一个析构队列。在执行垃圾回收的时候,如果对象不再存活但定义了析构函数,那么对象会添加到析构队列并标记存活。本轮GC将不再回收这些对象,而是在GC结束后由析构线程执行队列中的函数。析构线程执行完的析构函数的对象,将会由GC在下一轮回收中回收。

STW

“标记并清除”这种形式的GC会确定那些对象正在被程序使用。也就是说我们需要扫描对象之间的关系。也就是说我们需要遍历对象包含的所有引用类型的成员。因为成员值会随着程序的运行而不断的修改,那么对象之间的关系就会随着程序的运行不断的改变。这样就会给执行GC的线程与执行其它处理的线程同时

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:在Java编程中,当出现"java.lang.OutOfMemoryError: GC overhead limit exceeded"错误时,意味着Java虚拟机(JVM)花费了太多的时间在垃圾回收上,但回收的内存却非常有限。这个错误通常是由于堆内存设置过小导致的。\[3\]当垃圾回收占用了超过98%的时间,并且回收的堆内存不到2%时,就会抛出这个异常。这种情况下,JVM会认为垃圾回收的效果不好,进而抛出异常。引用\[2\]中提到了解决这个问题的方法。一种解决方法是增加JVM的堆内存大小,可以通过修改JVM的启动参数来实现。另一种解决方法是检查代码中是否存在内存泄漏或者过度使用内存的情况,例如循环引用、大量的对象创建等。通过优化代码,可以减少内存的占用,从而避免这个错误的发生。所以,当出现"java.lang.OutOfMemoryError: GC overhead limit exceeded"错误时,可以考虑增加堆内存大小或者优化代码来解决这个问题。 #### 引用[.reference_title] - *1* *3* [java.lang.OutOfMemoryError: GC overhead limit exceeded问题分析及解决](https://blog.csdn.net/whc888666/article/details/128496598)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Java IDEA pom 报错java.lang.OutOfMemoryError: GC overhead limit exceeded 基本所有依赖都报错(除了JDK...](https://blog.csdn.net/weixin_45268865/article/details/123839339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值