00:00
接着啊,咱们来看第二个概念啊,叫做内存泄露,那因为呢叫做memory Li啊内存泄露那也称为呢叫做存储渗漏,当然这个词呢,咱们其实很少叫啊,所以更多的时候呢,咱们说的还是叫内存泄露,那么内存泄露这个概念呢,在面试当中啊,实际上也经常被问到啊说呢,你理解Java中的这个内存泄露的概念嘛,你能举例子吗?对吧?哎,这呢我们是非常常见的,那如果说呢,让大家去举这个叫内存溢出的问题,我想呢,所有的同学啊,肯定都能够轻而易举的举出来,对吧?但是关于这个内存泄露的话呢,想必很多人呢,还是不太清楚,那这呢,咱们就诶通过这样的几个PPT,咱们彻底的把这个内存泄露的这个事儿呢说清楚,以及呢,哪些错误的例子?这个内存泄露例子你不要举,这个咱们都要说出来。就有可能大家呢,关于这个内存泄露不清楚,你就去网上去搜,那网上呢,我也看到一些哈,帖子也好,视频也好,实际上呢,关于这个内存泄露的这个例子的举的呢,是不对的啊,这个大家一定要小心一点,那下边咱们来看一下何谓呢Java层面的内存泄露,那这儿呢,我从两个意义上来说啊,严格上来讲,注意严格上来讲说只有对象呢,不会再被程序用到了。
01:07
啊,不会再被程序用到了,但是呢,这个GC呢,又不能够回收他们的情况,咱们呢才称为叫做内存泄露。啊,这句话的话呢,看似简单,但实际上呢,需要大家呢细细的去体味一下,就是对象呢我不用了,但是呢,这些呢又回收不了,哎,这呢就称为叫做内存泄露。那我还是举一个呃例子,但是这个例子似乎呢,又不是那么的恰当啊,哎,但是也还能基本上去吻合一下,就是我们刚才也提到了,说这个房子的问题,对吧,比如说呢,我买了个房子,这个房子是80平,那80平这个房子里边儿,其中有十平米呢,是公摊,咱们知道买房子都会有这个公摊的,对吧,这个OK,行,那么这个十平米的公摊呀,你想想这个数据是吧,或者这个面积咱们实际上呢是不用的,或者咱也用不了,对吧,就相当于说这个对象呢,不被这个程序使用了,就我们也用不了,但是的话呢,咱们又不可能在买房子的时候呢,说你给我把这个公摊呢给去掉,我只买那70平。
02:01
那我只掏那70平的钱又不行,对吧,那就相当于是呢,我们去呢也去不了,就相当于这个GC呢,想回收也回收不了啊本身呢,我们又没法用,或者说也不用了,但是呢,这个去呢又去不了,那就很恶心,那这个事儿呢,就有点儿像我们说的叫内存泄露一样。啊,有点这个像什么,就是脚底下前几天这个是嗯,环球时报的那个总编辑啊,形成澳大利亚,就说说澳大利亚,现在就像中国脚底板呢,踩了一个这个叫什么口香糖一样啊,甩也甩不掉啊,但是呢又很恶心,也不用啊,啊那就有点像我们说的这个内存泄漏一样。嗯,就是这个问题好,这个呢,是从咱们从这个严格意义上来讲,内存泄露的概念啊,一会儿呢,咱们给大家举例子啊,啊接着往下说说,但是这个实际情况呢,很多时候呢,我们有一些不太好的这个实践啊,代码的编码习惯或者一些疏忽,导致呢,有一些对象的这个生命周期啊变得很长,甚至呢,呃,也导致了这个很长的对象呢,存在最后出现了OM,那我们也可以把它们理解成叫宽泛意义上的内存泄漏。
03:05
那这个大家能不能去理解呢?就这呢是严格上的讲,这个呢,是更宽泛意义上的讲,那比如说啊,我们先举举这样的例子哈,比如说呢,我们定义这个变量的时候呢,这是一个类,这是一个方法,我们本身呢,其实可以把这个变量呢,定义到我们这个方法内,作为一个局部变量出现,这样的话呢,我们这个对象呢,这个变量呢,就是你出了这个方法呢,它其实就要被回收了,对吧?那但是呢,你给定义成一个成现变量了,那生命周期就会长一些,甚至候呢,你把这个成现变量还定义成是一个叫static了,那这时候呢,就意味着我们这个静态的变量,或者叫类变量,是不是随着类的加载而加载,随着类的消亡而消亡,那这个变量的生命周期就会非常长,那如果说我们在这个程序当中出现大量的这样的生命周期很的对象的时候,就有可能最后啊,加上我们还有一些这个没办法被回收的这些这个数据的存在,最后呢,出现了OM,咱们把这个生命周期很长的这些对象本身没有必要这么长,而且你又生命很长的,咱们也可以理解成宽泛意义上的内存泄漏。
04:03
哎,宽外以上的这个泄漏,再举个例子,比如呢,咱们在写这个外部程序当中的时候,我们经常呢,会把一些对象数据是不是存储到这个应用程序,或者叫绘画级别,对吧,那本质上呢,其实又没有必要给它设置成这样级别的,最终呢,导致这个对象的生命周期呢,就很长,咱们也可以理解成宽泛意义上的内存泄漏。行,那么关于宽泛意义上的内存泄露例子呢,我刚才给大家举了两个例子,那你在面试的时候呢,可以去提,诶可以去提,OK,行,那下边呢,就提到说尽管的这个内存泄露呢,并不会立即引起程序的崩溃,那但是呢,一旦发生内存泄露了,程序当中这个内存呢,就会被蚕食,那如果说内存泄露的数据越来越多,越来越多啊,又回收不了,就很恶心是吧,最后呢,诶导致我们内存的耗尽,出现了OM。哎,这个大家注意,也就是说呢,内存泄露呢,是有可能导致我们说叫哎内存溢出的,但是这个你注意不是必然的啊,如果说我们这个内存泄露这个数据呢,没有那么多,那我们这个正常这个JC的话呢,也能够把我们这个内存空间呢,及时的去回收啊,再去分配新的对象,哎也可能出不了OM啊,就是说呢,你如果内存泄露这个数据比较多的时候,再加上我们其他的一些使用的这个数据加一起呢,结果超出了我们内存呢能承受的范围啊,才会出现这个OM啊,这个大家想必应该也都比较清楚啊行,那这里边咱们指的这个啊,说内存溢出了这个内存呢,主要指的还是我们这个叫虚拟内存啊,这个咱都知道,这个Java程序的话呢,咱们都是用在这个JA运行在这个Java虚拟机上的,咱们说的这个对空间啊,占空间这个方法区啊,咱们这个其实都是Java这个层面的虚拟内存对吧?诶这个大家清楚啊。
05:43
好,那么接下来的话呢,咱们给大家说一下这个内存泄露啊,从这个图示上来看,它到底是什么意思,这个呢两个图,我是从这个eclipse的官网上呢,把这两个图呢拿下来的啊,这个权威性呢肯定是够啊,当然这个我们这两个图呢,比较清晰的能够表达出来我们Java的这个内存泄露到底是怎么回事,当然呢,如果去举例子的话呢,呃,建议呢,你先去举这个严格意义上的例子,那下边呢,就咱们来说这个事啊,首先呢,你要想举这个例子说清楚了,你得先明白到底什么叫做内存泄露。
06:14
对吧?哎,什么叫做内存泄露啊,大家注意看我们这个图呢,其实很清楚的能够表达出来,咱们Java呢,所使用的这叫可达性分析算法,对吧?我们从这个j c root呢出发,诶我们呢去有这个引用料啊,关联到相关的一些对象啊,那凡是呢,关联关联到的这个对象呢,我们都认为是可触及的,也就是呢,我们是可达的对象就不能回收,那像这个数据呢,显然是不是要被回收了。对吧,这叫回收,这就咱们前面说的,你这个可能它的这个,呃,按照说这个我们前面讲的叫引用计数算法是吧,那个计数的话呢,可能不是零,当然呢,由于跟我们这个这些root呢不关联,所以呢,诶我们就把它们回收掉。有点像这个循环引用了是吧,哎,就它啊把它呢就回收掉了,行,那么现在的场景是什么呢?就是我们呢,通过这个指针在引用的时候呢,诶发现呢,我们后期有一些对象啊不用不用了啊,就是我们这一波对象了不用了,所以呢,你看有一些指针呢,就及时的给断开了啊及时给断开了,但是大家会发现呢,我们这儿呢,有一根儿这个指针呢,没有断开。
07:14
这个指针没有断开,没有断开呢,就会导致呢,咱们这个GC root呢,你看是不是通过这样的指针的方式呢,我又能够找到这样的诶几个对象了。对吧,又能找到这几个对象,那就导致呢,我们这几个对象呢,是不是就没有办法呢,被回收了呀,但事实上呢,这几个对象就这一波的这个对象啊,我们都不用了。哎,我们有些指针断开了,但是你有这个指针没有断开,这个指针的存在呢,就会导致我们这个这个这几个对象呢,就没有办法呢,被JC掉,所以呢,诶你看这块就写着啊,你忘记断开的这样一个引用呢,就导致了我们的memory link。啊,写的是不是非常的清晰是吧?哎,就是它的存在导致的,哎,这就是我们狭义上所讲的说呢,就像不用了,但是这些呢,又没办法回收,这不用了,但是回收不了,因为呢,你这个指针存在啊,它还是可达的。
08:02
OK,哎,就是这样的一个情况啊,那么说到这儿的话呢,大家要注意网上的一些错误的例子。啊,为了说明这个错误的例子呢,我再打开一下咱们前一章讲的这个叫引用技术算法啊,引用技术算法,这个引用技术算法这块呢,我们说它有一个叫循环引用的问题,对吧?诶你看我在这个图里边也写了一个内存泄露,当然咱们讲这个引用技术的时候呢,我提过说大家呢,不要举这个内存泄露的例子。不要举这个内子泄露例子,因为呢,咱们Java程序是不是根本就没有使用这种引用技术算法呀,所以你举这个例子呢,是不合适的啊,那网上呢或一些视频当中啊,它就是举这个例子了啊,它这个例子怎么说的呢?说呢,我们现在呢,有一个链表啊,这是第一,我们这其实是个引用了啊,你也可以说成第一个元素也行啊,我们这个引用的话呢,指向了这样一个对象,这个对象呢,这个指针依次往下啊去引用,然后现在的话呢,我们把第一个对象这个指针呢给它去掉。那去掉以后的话呢,呃,那这个指针不存在了是吧,但是你后边这几个对象的话呢,他们还有指针指向了,那他们呢,我们说就构成了一个内存泄露。
09:09
啊,就相当于是我们这个图里边所描述的这个场景,对吧,注意这是一个错误的情况。这个错误的,为什么说错误呢?因为这种情况啊,咱们针对的是引用计数算法,你呢叫做内存泄露。但是咱们Java呢,是不是根本就没有使用应用技术算法呀?啊,那人家面试说说你能举出Java中的一个内存泄露的例子吗?然后这块你就说啊,我这个有指针,然后呢,第一个指针断掉以后,后边这几个呢,还相互引用着,哎,这个呢也不用了,但是又没办法回收,所以叫内存泄露,注意是错误的。啊,面试官呢,有可能他也不会,呃,跟你纠正这个错误,他就那呵呵的一笑是吧?淡淡的一笑,啊说呢,这个小伙呢,呃,掌握的不太行啊,这个不懂,到底呢,我们Java中用的是什么算法啊,你得举的例子呢,是错误的,他可能也不给你明说,但是你也不知道是怎么回事是吧?啊诡秘的一笑,啊,这个注意不能举这个例子。
10:03
那不能举这个例子,我们应该举什么例子呢?哎,先基于大家关于这个重呢,理解清楚以后,哎,我这儿呢,给大家举了两个例子啊,举了两个例子好,那首先我们看第一个例子啊,这个例子呢,我这写的叫单利模式。嗯,那单利模式呢,咱们都知道啊,就是我们创建了一个类,这个类类里边只有为一个对象,这个对象呢,它的声明是不是叫static呀?啊,那举个例子的话呢,像咱们刚才那会提到了这个runtime.get runtime,这个runtime呢,它其实就是个单立的设计啊。哎,Run time行,把它打开。你看这个runtime里边这呢,就是它唯一的这个实例,那声明的是一个静态的,那这个run呢,代表的就是我们说的运行时,呃,数据区内运行时这个环境啊,每一个应用程序或者每一个进程呢,就这一个实例。啊,那这个对象的话呢,它会随着咱们这个呃程序的执行而产生啊,随着我们这个程序的结束呢而终止,所以它的生命周期呢是很长的啊,那么在这一个比较长的对象的生命周期当中,如果你拿着这个对象呢,呃用它呢,又去关联了另外一个外部的对象的话。
11:08
也就是说这是咱们这个单立的这个对象,你现在呢,又让他去引用了另外一个对象,而这个对象呢,本身呢,咱们用用一段时间就不用了啊,就不用了,哎但是呢,由于你这个哎单立的这个对象的生命周期非常长,导致呢,这个指针呢,你也没有及时的去给它断掉,所以呢,就连带着是不是它的这个外部对象的生命周期也很长是吧?哎你这些的话呢,还回收不掉,它本身不用了,但是还回收不掉,这呢就叫做哎内存泄露。那这叫内存泄露,当然呢可以举这个例子,那还可以举什么例子啊,就像我们提到了说这个我们有一些资源呢,提供了close方法,当然呢,我们又没有及时的去调这个close方法,去关闭这些资源啊,也会导致内存的泄露,那比如说叫数据库连接啊,网络连接和IO连接操作,我们都需要手动的去close。啊,这其实这其实想描述的什么呢?就是凡是我们跟外部的一些资源,比如说你跟数据库也好,跟这个网络中的资源也好,还是跟我们这个本地的资源也好,凡是呢,你需要呢,进行这个资源的一个交互的时候呢,最后都需要手动的做一个资源的这个关闭,这个时候呢,我们这些的时候呢,才能够去回收你这些对象,否则的话呢,这些对象呢,由于没有关闭外部的资源连接,它就不能够及时的被回收了,啊这整个你这个程序结束的时候,我们才能回收是吧?那你在不使用的时候呢,不能及时回收,这不就是一个内存泄漏吗?
12:28
啊,就是这样的例子。行,那么通过咱们刚才这样的几个PPT的这个描述啊,希望大家对这个内存泄露的真正在Java中的这个原理是什么。是吧,从狭义上和广义上的讲解,包括呢,真正的这个内存泄露的例子应该怎么去举,这个大家要清楚啊,真正在面试的时候呢,哎,这个时候呢,你得能够答的是不是到位一点是吧?哎,避免呢时网上的一些视频啊或一些帖子上这个误导啊,最后呢,答出来的是一个错误的情况,这个呢就很尴尬了是吧,好,那这呢,就我们关于这个内存泄露的一个说明。
我来说两句