vlambda博客
学习文章列表

C#和Java之内存管理(GC)比较

    有过C,C++开发经历的同学,肯定对当时碰到的内存访问越界或者内存泄漏深恶痛绝,哪怕后续有了智能指针这些东西,还是不能完全避免此类问题。

    而C#和Java拥有的自动内存管理机制,让程序员可以不必自己去管理内存,专注于功能开发。

    所谓内存管理,必然是运行时的事情, 而C#和Java之所以可以做到自动管理,就是因为它们在真正的机器二进制OS上有了自己的运行时(虚拟机)。

    所以首先可以看一下他们各自的运行时, C#的CLR 和 Java的JVM 

CLR vs JVM

 下面的流程图,基本描述了C#和Java总体的一个从源代码到最终的可执行的过程。

源代码通过各自的编译器,编译成中间件的编码,这些中间件的编码可以被各自的运行时认识并管理,运行时载入中间件编码,转换成机器二进制运行在操作系统上。

 运行时的内存区域

   要明白内存管理,首先必须知道运行时各种内存分配情况。

其实总体来说,CLR和JVM中的内存分类基本一致,只是各自的逻辑划分和称谓有些差异。

比较大的两块分别是堆和栈。

栈都是线程私有的,每个方法在执行时都会创建一个栈帧用于存储局部变量、操作数栈、方法出口等等信息。栈的生命周期与线程相同,而栈中存放的内存,在各自属于的方法执行完成后,就会自动释放。

堆是所有线程共享的。几乎所有的引用对象实例都是存放在这个区域的。而CLR或者JVM中下了大力气来管理的就是堆这块的内存区域,这种管理机制一般被称之为垃圾回收。

一般来说,还有方法区(用于存储已经被加载的类信息,即时编译后的代码等数据,CLR中一般称做Type Object 区,JVM中叫Method Area),运行时常量池(存放各种编译时生成的字面量符号引用等信息),这些都可以认为是堆中的逻辑分块。

 垃圾回收机制

垃圾回收机制就是用来管理堆中的内存了,当堆中的某个对象不再被使用到的时候,可以自动的将这块内存回收,置成可用状态。

可达性分析

那么如何知道某个对象不再被用到了呢,这个一般都是基于可达性分析后生成可达对象图来实现的;可达性分析就是通过从一些根对象出发,去遍历每一个被引用到的对象,当一个对象到根对象没有任何引用路径的时候,就认为该对象可以被回收。

根对象一般包括:栈中定义的局部变量,全局的静态对象等等。

而这里说的引用关系,其实是指强引用。而和强引用相对的,还有弱引用。

强弱引用

弱引用的引入其实是和垃圾回收器紧密相关的,因为垃圾回收的促发点是难以掌控的,而有一些对象的创建确实需要很大开销的,并且这些对象也不需要非常频繁的访问和很长的生命周期;那么作为平衡,就有了这个弱引用机制。弱引用即表示引用的对象可以被垃圾回收正常回收,但是如果在垃圾回收回收它之前想用它,还可以通过弱引用重新拿回来这个对象进行使用。

CLR中只有强引用/弱引用之分。

而JVM中除了这两个之外,额外还有软引用和虚引用两种:

软引用是用来描述一些还有用但是非必须的对象,被软引用关联着的对象,只有在发生outofmemory的情况下才会被回收。

而虚引用不对对象的生命周期和可达性分析产生任何影响,它唯一的作用是提供一种机制,可以在该对象被回收的时候收到一个系统通知。

 回收算法

上面提到,通过可达性分析可以标记出所有需要回收的对象。那么如何回收呢?

另外一种方法,就是需要对内存进行压缩处理,这种方法可以大大提高内存的利用率,并且为后续的内存分配带来便利(之后从已用空间的后面追加分配即可)。

对于内存的压缩处理,其实也可以分为两种处理手段,复制和整理。

复制算法就是把还存活的对象复制到另外一块内存中,它不太适合垃圾回收时有大量存活对象的情况,因为在这种情况下会需要准备大量的可用内存空间供复制使用。但是对于很少有存活对象的情况下,此种算法非常高效。

整理就是在清楚了可回收对象后,将存活对象的位置移动,使得他们的内存成为连续的一整块,这种算法在很少可回收对象的情况下使用比较广泛。

分代

我们上面提到了各种不同的回收算法,各自适用与不同的场景。而CLR和JVM中分别通过对对象存活周期的分组来适配这些场景,然后每种场景去适用最高效的回收算法,这种分组称为代。

CLR中有三个代,分别是第0代,第1代,和第2代。每次促发垃圾回收后仍然存活的对象自动上升到下一个代。

JVM中有两个代,分别时新生代和老年代,新生代中一般还有两个区域eden区域和Survior区域,对象首次分配时都在eden区域,每次垃圾回收时候,如果一个对象还存活,会被复制到Survior区域,并且年龄+1,年龄到一定程度后就会进入老年代,如果新生代中的Survior区域的内存在GC时不够用了,那么这些存活的对象会直接进入老年代。

还有一个大对象堆一说,其实大对象堆在CLR中可以被认为也在第2代中,JVM中大对象在老年代中。

一般来说新生代或者第0代,都是采取的标记-复制的算法,而第1,2代或者老年代采用标记-整理的方法比较常见,当然大对象一般采用标记-清除的方式

出处:https://www.cnblogs.com/diufeng/p/7156511.html


支持小微:

腾讯云 爆款2核2G云服务器首年40,2G4核云服务器298元/3年

链接:https://curl.qcloud.com/1VVs7OBH


右下角,您点一下在看图片

小微工资涨1毛

商务合作QQ:185601686