JAVA语言——垃圾回收

Posted by 冷眼樵夫 on 12-21,2018

现在编程已经非常平民化了,不像之前那都是计算机天才干的事情。比如操作计算机的内存和寄存器的使用,甚至cpu的操作等等。现在编程使用高级编程语言,使用接近于人类自然语言进行编程,就像是写博客一样(当然还没有那么夸张),这是多么的平民啊。关于计算机的底层硬件操作,命令处理,完全不用你操作,你要做的,只是按照事先规定好的语言规范,写下要做什么的指令,其他的,交给编译器,运行平台,操作系统,就ok了。

当然,编程还是有很多工作要做的,不然程序员都要失业了。比如,内存的操作。

等等,不是内存的操作不用程序员(使用高级编程语言)操心了。要说明的是,这里的内存操作指的是比较底层的,比如如何高效的使用,怎样分配内存,释放内存空间,查找数据等等。但是最起码,要做什么,你得发布指令吧。你的说出你要做什么,其他的怎么做,就不用你在费心了。

你编写的代码,一定意义上,就是发出指令,我要内存空间放数据,这个数据使用完毕,空间不再使用了,我要拿到某个内存空间存放的数据等等,计算机的cpu和内存配合来完成。那么,使用完毕的内存空间,就是计算机系统中的垃圾了,得及时进行清理,就是我们要说的垃圾回收问题了。

在Java产生之前,其他的编程语言,比如C,C++等,是由程序员发布指令,来进行内存空间的分配获取和取消释放的。大部分时候,这是一件很困难的事情。因为你并不总是事先知道内存应在何时被释放,特别是写大段的代码时候,当你写完那些复杂的逻辑后,有关之前索取内存的释放问题,早就抛到九霄云外去了。但是就像是只有索取没有给予时候(其实内存来说你只是归还而已),最终都是没有好结果的!当在系统中内存都被索取一空,直到没有能够被分配的内存时,就可导致程序瘫痪,这种程序被称作具有内存漏洞的程序,程序的健壮性就无从谈起了,你还能指望用户心平气和的使用一个是不是就崩溃无响应的软件么?这个就是进行垃圾回收的必要性了。

举个例子,你上班了,你漂亮的办公桌旁边放着一个纸篓,你可以很顺手的将你工作中产生的垃圾扔进去(就像你已经使用完毕的内存空间,内存空间还是有用的,无用的是占据着内存空间的垃圾数据),但是如果你只是向里面扔垃圾,而不及时进行清理呢?谁都知道结果,你被大量的垃圾环绕,特别是夏天,你会有一个“回味无穷”的工作空间,大家纷纷避而远之。

那怎么办呢?谁都知道,去把垃圾倒掉,把纸篓空出来。这就像写代码,把垃圾数据从内存空间清理,恢复内存空间的清洁。但是当你忙着工作,然后加班到很晚的时候,这个事情很有可能就会忘记了,就像是前面写了大段代码之后,你拖着疲惫的身体回家,垃圾的清理?管他呢,然后第二天来了之后,你的办公桌的味道,嗯,肯定是不怎么样的。

那怎么办呢?(又说了一遍。。。)大部分的公司还是很人道的,他们会有专门的人来做这些工作,不用你操心,你只管扔,第二天来上班之后,你得到的仍是一个清洁的办公环境。

这就像是Java做的一样。你不用在操心什么时候去倒垃圾,Java平台把这个工作揽下来了。你只要在需要的时候和Java平台要内存空间,操作数据。当使用完之后,这些垃圾你可以完全无视它,我们的Java平台会帮你做善后处理的,它会及时发现垃圾内存空间的存在,并及时清理,还你一个整洁的内存空间。

这个说起来容易做起来难,不然也不会直到Java才着手做这件事情,将广大程序员从内存泄露的噩梦中拯救出来。比如,如何确认该内存空间是垃圾?什么时候做垃圾的验证,验证之后,是立即清理呢还是定时清理,还是在特定的时机清理呢?就像是公司的清洁人员一样,如果他一直就呆在你旁边,实时清理垃圾,你受得了么?那么他十天半个月清理一次,你受得了么?估计都够受的。

我先简单说说Java如何验证垃圾,这个验证方式是比较早期的,但是比较好理解。比如当你想Java申请分配内存空间存放数据后,平台将该内存数据标记一个标志,标记为1,意思是有一个地方在使用该数据对象。当再有其他部分使用该数据对象时,这个标记不断累加。当Java发现这个使用已经过期后,相应的递减标记。这样当标记为0的时候,就意味着这个数据对象无人使用,即垃圾了。

至于什么时候进行自动垃圾回收,按照Java官方的解释,有两个时机,会进行垃圾的自动回收。

1.当平台没有其他的线程运行的时候,也就是说,现在java平台很闲,它得找点事做,这个时候,可能会执行垃圾回收操作。

2.当平台系统可用内存量过低,无法保证基本的内存空间分配操作时候,它可能会突发地执行来挽救内存资源。当然其执行与否也是不可预知的。也就是说你的纸篓已经满了,无法再向里面扔垃圾了,这时候,就需要保洁人员来处理你才能继续扔垃圾啊,但是保洁员来不来,什么时候来,你是做不了主的。

以上都是比较口语化的说明,下面就是比较正式的说明了,放在这里了。

  1. 垃圾回收器的工作目标是回收已经无用的对象的内存空间,从而避免内存渗漏体的产生,节省内存资源,避免程序代码的崩溃;
  2. 垃圾回收器判断一个对象的内存空间是否无用的标准是:如果该对象不能再被程序中任何一个“活动的部分”所引用,此时我们就说,该对象的内存空间已经无用。所谓“活动的部分”,是指程序中某部分参与程序的调用,正在执行过程中,尚未执行完毕;
  3. 垃圾回收器线程虽然是作为低优先级的线程运行,但在系统可用内存量过低的时候,它可能会突发地执行来挽救内存资源。当然其执行与否也是不可预知的;
  4. 垃圾回收器不可以被强制执行,但程序员可以通过调用System.gc()方法来建议(注意,只是“建议”)执行垃圾回收器;
  5. 垃圾回收器不能保证一个无用的对象一定会被及时收集,也不能保证垃圾回收器在一段Java语言代码中一定会执行。因此在程序执行过程中被分配出去的内存空间可能会一直保留到该程序执行完毕,除非该空间被重新分配或被其他方法回收。由此可见,完全彻底地根绝内存渗漏体的产生也是不可能的;
  6. 我们没有办法预知在一组均符合垃圾回收器收集标准的对象中,哪一个会被首先收集。也就是说,垃圾回收的次序是不确定的;
  7. 循环引用对象不会影响其被垃圾回收器收集;
  8. 为了更快的让垃圾回收器收集不再有用的对象,可以通过将对象的引用变量(reference variables)设置为null值,来暗示垃圾回收器来收集该对象。

这里重点说明的是,虽然就像你无法直接命令保洁人员倒垃圾一样,你无法直接操作Java进行垃圾回收操作,但并不意味着你对此无能为力。比如上面的第四条,System.gc()方法,但是可以很负责任的说,它,基本,没有用。特别是相对于第八条来说,人家还来得跟实惠些。就是将对象的引用变量(reference variables)设置为null值。至于什么是对象,什么是引用变量什么是null,那些都是浮云,以后你自会清楚地。不过难免有的笔试题里面,特别是有的公司会出一些Java的古老的面试题,你还得直到这个方法。

另外,这里先补充一个编写Java程序的原则:对于那些不需要的对象,不要再引用它们!程序的执行极大受到可用内存的影响,可用内存越少,那么垃圾收集的执行次数越多,这将极大地伤害性能。


0评论