关于class的GC问题

Java中类的生命周期与局部变量分析

源文见:http://www.iteye.com/problems/8266

 

首先要区分清code和data。以后JVM很可能会有新的机制来允许方法(code)被GC,例如说通过新的AnonymousLoader、MethodHandle等机制。但就现在的JVM来说,code一旦被ClassLoader给load进来之后就放在PermGen heap上,基本上直到程序终止前都不会被GC了。据说Java 6 Update 4还是多少开始有个新的启动参数,-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses,不过没仔细看不知道到底能达到怎样的效果。 

Java中的code是以类为单位保存在class文件里的。Class文件里含有类的名称、继承关系、常量池,以及各个静态/成员方法的字节码。JVM在加载程序的时候也是以类为单位来加载的,code会被加载到称为"Permanent Generation"(PermGen)的堆上。 

类的静态变量是在其被加载时先分配空间(可以认为也是在PermGen里),然后初始化。其生命周期与类的code的生命周期一样长。 
成员变量则是在new新的实例时分配空间,并由默认初始化器和构造器等初始化。其生命周期与实例的周期相关。注意这里说的生命周期是单纯指primitive type或者reference自身,而不包括reference指向的对象。举例来说,假如一个实例a的成员x被赋为null了,那么这个null值仍然要保存在a里,在32位系统上这就意味着至少占用了4个字节。 

方法中的局部变量概念上说是在栈上分配空间的,其生命周期与方法调用的周期一样,也就是说方法调用过后空间就会被回收。注意这里指的同样是primitive type或者reference自身,而不包括reference所指向的对象。再举个例子的话,假如在方法里有这么一句:

Java代码:
  1. String s = new String("abc");  

则"abc"是在常量池里分配空间的,s现在所指向的刚new出来的对象(作为一个实例)是在第0代堆里分配空间,而s(作为一个引用)自身概念上是在栈上分配空间的。方法结束时,s自身的空间会被回收,但s所指向的对象是否被回收取决于是否还存在指向它的活引用。 

之所以说是“概念上在栈上分配空间”是因为JVM有可能会将局部变量优化为直接分配在寄存器上,不过这是实现细节,对上层的程序员并没有可见的影响。 

基本上就是这样吧……
### Unity GC 垃圾回收机制面试问题解析 #### 1. Unity中GC的基本工作流程是什么? Unity使用的垃圾回收机制基于.NET运行时(CLR),其基本流程包括标记和压缩两个阶段。在标记阶段,GC会检查堆上的所有对象,并标记所有不可达的对象为待回收。随后进入压缩阶段,GC会整理内存碎片,移动幸存的对象到连续的内存区域,并更新所有根的引用以指向新的内存位置。这一过程会暂停所有线程,确保回收的准确性[^2]。 #### 2. Unity中GC的分代回收机制是如何工作的? Unity的GC采用分代回收策略,将对象分为三代:第0代、第1代和第2代。新创建的对象属于第0代。当第0代内存达到阈值时,GC会回收第0代中的不可达对象,幸存的对象升级为第1代。当第1代内存达到阈值时,GC会同时回收第0代和第1代对象,幸存的第1代对象升级为第2代。第2代对象是长期存活的对象,只有在内存不足时才会触发完整的GC回收[^2]。 #### 3. 为什么频繁的GC会影响Unity游戏性能? 由于GC在标记和压缩阶段会暂停所有线程(Stop-The-World机制),频繁的垃圾回收会导致帧率下降,影响游戏的流畅性。此外,堆上的对象越多,对象之间的引用关系越复杂,GC需要处理的数据量越大,导致回收时间增加,进而加剧性能问题[^1]。 #### 4. 在Unity中如何优化GC的性能? - **避免频繁创建和销毁对象**:使用对象池(Object Pool)技术重用对象,减少GC的触发频率。 - **减少堆内存分配**:避免在Update等高频函数中分配临时对象,例如字符串拼接、LINQ查询等。 - **及时释放引用**:当对象不再使用时,将其引用置为`null`,以便GC能够识别为垃圾对象[^3]。 - **使用结构体代替类**:对于小型数据结构,优先使用结构体(值类型),避免堆内存分配。 - **避免循环引用**:使用弱引用(WeakReference)打破循环引用,防止对象无法被回收[^3]。 #### 5. Unity中GC的触发条件有哪些? - **显式调用**:使用`System.GC.Collect()`强制触发GC,但不推荐频繁使用。 - **内存分配达到阈值**:当第0代对象的内存使用超过预算时,自动触发GC。 - **资源加载或卸载**:加载或卸载资源(如AssetBundle)时可能触发GC。 - **脚本编译**:在Editor中修改脚本并重新编译时,会触发GC以清理旧的类型信息。 #### 6. Unity中有哪些常见的GC优化技巧? - **字符串拼接优化**:避免使用`+`操作符拼接字符串,推荐使用`StringBuilder`减少临时字符串对象的创建。 - **协程与IEnumerator优化**:避免在协程中频繁使用`yield return new WaitForSeconds()`,可以使用共享的协程或自定义计时器。 - **匿名函数与闭包**:避免在频繁调用的函数中使用匿名函数或闭包,因为它们可能隐式捕获变量并延长对象生命周期。 - **使用Unity内置的容器类**:如`Unity.Collections`包中的`NativeArray`、`NativeList`等,它们在C# Job System中避免了GC分配。 #### 7. Unity中如何检测GC的性能影响? - **使用Unity Profiler**:通过Unity的Profiler工具监控`GC Alloc`指标,查看每帧的内存分配情况。 - **日志输出**:在Editor中启用`UnityEditorInternal.InternalEditorUtility.GetTotalAllocatedMemorySize()`等API,跟踪内存使用。 - **性能分析工具**:结合VisualVM、dotTrace等第三方工具分析C#堆内存的分配与回收情况。 ```csharp // 示例:使用StringBuilder优化字符串拼接 using System.Text; public class GCExample : MonoBehaviour { private StringBuilder sb = new StringBuilder(); void Update() { sb.Clear(); sb.Append("Frame: "); sb.Append(Time.frameCount); Debug.Log(sb.ToString()); } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值