00:00
呃,别的废话我就不介绍了,好吧,我们今天呢,呃,开始今天的内容,今天呢,我主要给大家分享。Java之中的常见的一用类型,这里面呢隐藏了一个特别麻烦的面试题,这个面试题呢叫S标local啊嗯。好。因为大家这个几十秒的时间,你可以读一读这道题啊,就这二中的引用类型到底有哪几种。每种引用类型的特点到底是什么?没有引的应用场景是什么,就应用在哪些方面是什么,为什么。然后。用在什么地方?呃,重点是后面local你了,不了解在什么地方。它会不会产生内存泄露,好吧。好了,开始今天内容。我们先从引用这件事讲起啊,因为今天呢,另外一个直播间呢,有咱们小白的同学们,然后再听老师的课啊,所以这块呢,我稍微的也要照顾他们一点点。
01:08
呃。这是什么叫一个引用,其实在Java里面呢,我们写任何的一句话啊,常见的比方说object o等于new object好这个东西呢。这个小O就是个引用,那么在内存之中呢,它会存在某一个地方是吧?这个地方存了个什么东西啊,存了个地址,根据这个地址啊,我们会找到一个大个的O的对象,好了,这个东西就叫一个引用,就是根据一个地址找到一个对象。在其他的语言里叫指针,OK。指针说指针大家更更加明确一些,只不过Java里面这个指针啊,它有好几种类型,有哪几种类型呢?基本上四种啊叫强软弱虚,一共四种,我们一个一个来讲好不好。
02:02
今天我们我们我们先来讲这个强引用啊,强引用这件事儿呢,其实比较简单,就是大家伙你们日常写的任何一个对象都是强引用对象。啥意思呢?比如说我们这里有一个类这个class,这class叫M。它叫什么不重要,这里面呢,我重写了它一个方法overri,这方法叫finalize,你平时很少用,也基本用不着,我给我我给大家重写这个方法呢,主要是说我们要了解一个普通的强应用对象啊,它的被回收的死掉的这么一个过程,好这finalize方法什么时候会被调用呢?大家都知道啊,就是这关于JVM这件事儿。GVM内部呢是这样的。就是这画图怎么费劲呢?好看这里啊。OK,这VM内部是这样的啊,这是我们JVM的内存,在内存里面装对象是吧,一个对象啊,M对象M对象M对象M对象装了好多,那么装了好多之后,如果说整块的空间被他占完了。
03:07
咱们有另外一个机制呢,叫做垃圾回收器,就会把它呢,把这些对象啊,从内存里头给它清掉,把空间给清出来,那每清掉一个对象的时候,这个对象会调用它的final方法。好,这是finalize它所起的作用,所以在这里头呢,呃,当一个对象被清除出去的时候,你会看到啊,这个finalize会被打印出来。我讲到这儿,大家应该能跟上吧,跟跟跟上扣个一来,嗯。处理回收之前调用,是的,没错啊,他没死的时候调用它final,嗯。OK,呃,大家认为这件事之后,我们先做一个小小的实验啊,看这个小程序,呃,这里呢,我们就一个M出来,那什么样的对象才能被回收啊。这这个简单讲两句,比方说这这有一个对象,那这个对象什么时候能被回收啊。
04:04
没有任何引用指向它,它就变成垃圾,没有任何引用指向它,就意味着这个对象一定是没用的对象。就没有任何任何引用指向你,你是不是就没用了,对不对,那怎么样才能做到说我原来有个引用指向它的时候,然后呢,最后把这个引用给它消除掉怎么办呀。小M设为空就可以了,从内存之中呢,大概就是这样的啊,这里有个小M,它指向了一个大M对象,然后我们把小M设为空,那小M里面就不装任何的地址了,这边引用就不存在了,那么对于这个大M对象来说呢?没有任何一种指向它了,OK了,它就可以变成垃圾了,垃圾回收器就可以把它干掉了,干掉它的时候,它会调用我们的finalize方法,那么finalize呢,就会被打印了,大概就这个意思。比较简单,好,我们怎么去,呃,让他。产生一个,呃,这个回收调用的过程呢,最简单的可以调用system.dc。
05:05
啊,然后他就会,呃,调用垃圾回收器帮我们回收,回收哪些回收哪些个不用的对象。好,然后我们把M打印出来,这肯定是为空的,主要我们看那个finalize会不会打印,试试看跑一下。嗯。走,你。好,大家看这里啊,这里头就把那个final给打印出来了,说明我们打M的对象被回收掉了。好,呃,这个就是简单的强引用,这个强引用是这样的啊,只要有引用指向这个对象。当然背后的原理比较比较多,我们简单理解为只要有用的引用指向它,不是那种循环没用的引用啊。A指向BB又指向A是吧?转圈子拉着手了,不是那种的一个正常的强的引用指向它,那么这个对象就不会被回收。
06:08
那么当一这个某个对象没有引用指向他了,这个对象就会被回收。好,这是我们普通的强引用啊,强引用比较简单,我就讲到这儿。下面呢,我们来聊这个第二个啊,当你了解了强引用之后呢,我们下面来了解什么呢?软引用。那么软也用是个什么东西?前景用这块没问题吧,可以继续的给老师扣个一好不好。啊。OK。好好,可以继续给老师扣个一啊。OK,那我们来看看这个软引用是怎么回事,这个软引用啊,实际上是一种特殊的强引用啊,咱们就就就不聊它背后是个强引用这件事儿,我们主要看内存里面这个软营是个什么东西。这个软引用是指的是一个引用被另外一个软对象所包装,这什么意思呢?你往这儿看就行了啊,往这看。
07:08
这个软也用的写法呢,叫soft reference reference是引用的意思,Soft是软软的soft reference。然后里边儿装了个什么内容呢?里边装了个bit类型的字节数组。好,看这里,在这里呢,我们拗了一个bit类型的字节数组,大家知道这个一个字节,这个1000个字节,1024个字节是多少啊,是1K对不对。然后1024K是多少,是一兆是吧?然后十再乘以1024兆呢。幺零这所是十,再乘以一兆呢,就是十兆啊,所以呢,我们这个字节数组,它大小是多少呢?它的大小是十兆。也就是说。我们在这里谬了,这个字节数组出来之后呢,我们内存里面呀。就有一个对象出来了,这个对象是一个字节数组,这个对象的字节数组大小是多少?是十兆的一个对象。
08:04
OK,好大家看这里,然后呢,它外面是用一个软引用给它做了个包装。这句话的意思是说,有一个引用指向了这个食道。但是这个引用跟普通的强引用不一样,它是个软软的引用,当然我这是逻辑上的灵魂画法。你理解它什么意思就可以,可是我们说我们用任何一个东西的时候,你往这看啊,就是我们指向这个十兆的这个东西是一个。软的是吧,这个应用是软的,但是我们soft reference本身是不是也是个对象啊,你看看。你把它当成一个普通对象,它名字叫M,是不是我们六个M出来相当于。那就意味着我们的SR这边还有一个对象,就是SR,它是一个强引用,指向了一个什么东西呢?Soft reference对象本身,在soft reference对象本身里面又包装了一个软引用,指向了谁?指向了我们这个十兆的字节数组?
09:05
好,我们重新画一下,看你是不是能理解啊,这有一个SR,对不对?它只相当于拗了拗了个新对象,这新对象是谁啊?S,这个soft reference对象。然后这新对象里面包装着一个软软的引用。这个原理又指向谁?指向了一个十兆的字节数组,这就是这句话完成之后我们内存里面的情况。好,这块能get到的老师扣个一好吧。啊,有啥用,别急,你先理解它是个啥,再来聊有啥用好不好,嗯。好嘞,好嘞,好嘞。然后我们怎么样才能拿到这个字节数组呢?大家是这么来拿的,就是调用这个reference对象的get方法。Get就直接get到了这个软引用对象,这个这个这个直接指向的这个软引用对象,就是这个十兆自觉速度。
10:00
呃。然后我们调用呃垃圾回收,看看他能不能把它回收掉,然后税500个毫秒,让垃圾回收器干活,干完活之后呢,看他是不是被回收掉了,好吧,跑跑看啊让。好,大家看看这里看第一个数出这个大块开头的B表示这是个字节数组,BAT类型的数组是吧,这说明我们能能够得到它啊,然后呢,我们。System GC,你会发现呢,这个当我们有了一次垃圾回收之后,它不会被他回收掉,如果回收掉的话。这里头就已经没东西了,是不是还能把它拿到啊好。在这跑这个程序的时候呢,给大家加两个参数,你注意看run。给他。加两个参数啊。
11:03
Program arguments。嗯。Vm options这是什么意思呢?就我我我我把这个虚拟机给它跑的时候啊,我给它加两个参数叫杠 XMS20兆杠XMX。20兆好,这句话什么意思啊,这句话意思就是说,呃,我我我这个小程序跑的时候,我把总的这个GGM空间啊,我给它设死了。看这里。就原来我GVM的空间很大,因为我这个本身机器内存就很大,所以它占很大空间,那这次我跑的时候怎么办呢?我把整个的空间就设为20兆。这个大家能看懂吧。这次跑的时候我就设20兆啊,这20兆呢,里边有一个大M对象,是不是M对象里边呢,有一个软软的指向了一个十兆的字节数左右,这哥们儿自己就占了一半。
12:07
OK,那另外一半还剩多少呢?另外一半还剩了十兆,但是这十兆里面装着一些JVM,他自己,呃,已经必须要要占用的一些内存,实际上这里面就有好几兆已经被占了啊,所以剩下的其实可用的空间是不足十兆的。嗯,好,讲到这儿可以跟上吧。来,能跟上给老师扣个一好吧,嗯。嗯嗯,不太难啊,我主要多问大家几句话的意思,就是照顾一下咱们刚入行的同学好吧。Ly run。嗯,好,大家注意看啊,我们第一次是能够就是我们呃,是能拿到我们的市料食料类型数组的,这个没问题啊,经过垃圾回收之后啊,也是还能拿得到,这个也没问题,但是呢。你看我们下面,我们下面干了一件事。
13:01
又干了这么一件事儿。我又分配了一个字节数组,这个字节数组是多大呢?12兆。好,看这里。那就相当于从空间里面,就相当于是这样的。我们把那些小对象忽略掉。这是我们整个的20兆空间。我说过JVM自己呢占好几兆是吧,这是GM自己占的,这个具体是占多少兆不好说,每个人空每个人的这个这个机器内存不一样的时候,它占的大小也不一样啊。好了,然后实际当中能用的呢,实际上是不足20兆的,我们假设是16兆,好吧,我们假设这VM自己占了四兆,然后我们这个原来那个软引用里面装的速度占了多少啊。占了十兆对不对,那现在我又要求你再给我占个12兆出来,同学们,你们看看这空间还够12兆吗?这不够了,就剩这么点了,大概还剩六兆,你12兆肯定是不够的,对不对。
14:01
我问你啊,如果说一个呃,这段空间里头12兆,你往里头做分配,整个空间不够它分配了怎么办呀,这时候出现一个错误,这个错误叫应该是正常的,叫OM,什么意思呢?叫out of memory的内存溢出是吧,内存不够使了,说白了。你得扩大空间才行,可是呢?你会发现我们这个并没有报错。然后这个十十二兆的空间已经被分配了,可是很不幸的是,你原来那个软的那个引用。It either get,你发现它已经为空了,看到了没有?说明这十招被干掉了,没了。那这个软引用是什么东西。我觉得大家伙儿就呼之欲出了,这软引用是个什么东西啊?这软引用就是。二流引用,人家一流的引用需要占空间的时候,如果空间不够了。
15:01
把你这个二流的给你干掉。所以我们总结一下软引用。软雄的特点就是什么呢?空间够的时候随便分配,空间够来往里装,后来我就来一大块头,人家是一一等的是吧,这个屋屋屋里的一一类人啊。这个三三宫,三宫六院里头的东西宫,正宫,正宫来了怎么办?清场,把那些非正宗的全部都干掉,把它放进去啊。来这块大概能听明白的,给老师扣个一没问题吧,啊这种就是软引用,嗯。对。好,那当然,呃,软引用这东西其实大概大家也就能理解了,当然下面我相信你们想问的是这软语有什么用啊?
16:01
是不是同学们软引用有啥用啊?啊,听我说啊,这个软饮用呢,只有什么情况下,呃,经常使用呢,就是缓存。我给你举个例子。给你举个例子,比如说呢,你写了一个图片处理软件,你从硬盘上装一张图片,装到内存里,大家都知道图片呢可能会非常大,这张图片呢,本身就是20兆夸机占了这么大一块空间。然后呢,你处理了处理。干别的去了,县城干别的去了。当你下次想继续处理的时候。你是不是直接从内存里头继续拿出这张图片来继续处理,这张是这个是最合适的,而不应该是把它扔到硬盘上去,然后再重新漏进来再处理,这是不合适的。所以呢,你每次装它的时候,把从硬盘里头把它装到内存里的时候,它非常费劲,这时候呢,优先把它装进来。
17:05
可是如果你用一个正常的。正常引用,强引用。那么就意味着这块内存啊,实际上永远不会被清掉。他又非占的地儿非常的大,又不能被清掉。那就很容易爆发内存异常,那就这个内存溢出。当别的有用的数据需要装进来的时候,他很快发现没空间了,图片太大了,装个几十张、上百张这样的图片,内存没了。所以呢,我们这里可以考虑用软引用把它装进来,用软引用装进来的好处是什么呀。不用的时候,空间够,他就在里边待着,不够了就把它干掉,让那些更有用的用的数据能够进得来。好,这块大家是不是能听懂啊。来听明白软语用它含义的给老师扣一好吧,嗯。
18:03
C加C加有有课啊,咱们C加是。呃,在那个IOT。AOT里啊,嗯。好,这就是软银的作用,它是非常适合缓存使用,这个面试也是会常问的,说你你你你确实了解什么叫软引用了。但是呢?它有什么作用呢?你就把缓存讲给他听。可以不用强饮用吗?如果你不用强饮用。有一些对象你是不能把它干掉的,同学们,你不用强引用,你不死了吗?是不是,嗯。好,我看看大家。呃,对于这个软引用有有没有一些什么样的问题,对比硬引用,对比硬引用没有硬用强引用,强引用是只要有,只要有引用指向它,它不会被干掉啊,这个强引用的和它的区别。说这个对象指向它的对象,指向它的引用是软的,这个对象呢,指向它的对象是强的,那么它的区别是什么呢?强的这个引用,只要是有这个引用指着他,他永远不会被干掉,如果你后来进来的对象说没空间了,没空间你就报错,你不要占我的地儿。
19:18
特别强硬,那软的呢,它就比较怂。如果你只用一个软,也用指着他,那后面的人说进来了,空间不够了怎么办?把你干掉。听懂了吧?嗯,Reference写法是固定的吗?是啊,就只只能用soft reference来包装它,OK。Windows分配的12兆也是软引用,会分配不成功吗?其实不,呃,这个你做你去做实验啊。应该是不成功不了,嗯。你去做实验吧,这个你自己做一个实验就行了啊,弱肉强食对啊。看看还有没有什么其他的。
20:02
问题。哎,这个小小反笑脸说,那阮也用这么重要的,以前的东西被干掉咋办?大哥,你都知道这东西是重要的,你还用软引用指着它吗?GC不能处理强引用吗?强引用指向对象不能被回收,那是有用的对象。你要干一件事,总是有用的和没用的,是不是?不能正来,进来之后把正宫娘娘先给干掉,这不行啊。有些妃嫔可以处理掉。如果同油存在很多软引用,先回收那些应该会全部全都干掉啊。或者说干掉同时位置,就看GC那边怎么设计了,这好像没有专门的说优先干掉哪个,不优先干掉哪个。老师好用还是idea好用?目前idea用的比较多。其实都不难用。
21:01
没有引用是指什么情况?没有引用是没有,没有任何一个。引用指向它,比方说原来M等于大M是吧,只指着一个大M,后来M等空了就没有了。好,我今天已经发现了,待会儿入门儿级的小伙伴儿比较多,我觉得后面听的课可能稍微费劲点儿,你要有点儿心理心理预期啊,嗯。Soft reference对象,这是个强引用啊,只有是把那个SR设为空的时候,或者整个程序退出的时候,它才这个对象才会被回收,但这个对象很小啊,它里面装的这个指向的这个对象才是真正的大对象,占空间的那个。嗯,下节什么课问一下咱们班班老师好了,这个如果没有和阮勇相关的问题,咱们就继续讲了好不好?嗯,软用比较简单啊,不太难。好,还有没有其他的问题,没没问题了吧,嗯。
22:03
嗯。引用是指针还是内存地指针呢?引用就是指针跟指针一样的啊。软引用是不是适合定时任务去执行?不适合。你这个任务是必须执行的任务,如果被干掉了,你任务不执行了怎么办?想什么呢,整天。好了,咱们可以继续了吗?可以继续给老师扣个一了,嗯。OK,好。就这里面有软引用啊,当然你得小心使用把,在他该使用的场场合去使用它,OK好,咱们重点我们讲第三个重点啊,第四个我不想讲了,因为大家伙感觉大家提的问题,从提的问题来看,大家都是菜鸟级别,我第四个给你讲完,你更更懵了,就。我们主要攻第三个,这是面试的重灾区,因为呃,虚引用实际上在面试中很少见,很少有人问这个啊,但是大多数的面试呢,集中在。
23:06
弱引用身上就是这个叫weak reference,我们主攻这个,好吧,这个weak reference是个什么东东?我们先看这句话啊,你把这句话读完,然后从脑子里头画,给我画出内存的图来。嗯。把这句话读完。然后呢,从脑子里头画内存图出来,你告诉我一声儿。怎么做?嗯。好,看这里啊,这个很简单。在我们。内存里面还有一个引用指针WR,指向一个weak reference对象是吧,这个没问题,好指向它,这是个强应用,但是它内部包装的一个。弱引用。
24:00
这也用不不是强的,也不是软的,它比较弱。指向这个M大M就行。好,这是整个内存的情况。OK。好吧,这块大家能get到的,老师扣个一没问题吧。嗯。对,主要是垃圾,垃圾回收的时候处理不一样,是的,各种饮用呢,主要是在垃圾回收的时候啊,它的处理的方式不一样啊,那么这个大家已经见识到了软用的处理方式了,软绒处理方式是,呃,你有空间你就占着是不是,哎没空间呢,没有空间的时候呢,你就给我,你你就给我,你就给我滚蛋,可有可无,是这个意思,但是我们来看若用的表现。若引用是这样的啊。呃,我们在里面装了个M对象,这个没有没有问题,然后我们打印这个Wl.get就是把那个M对象给它拿出来,然后打印出来,这个也没有问题,这个M是能打印出来的,我们来跑一下看。走,你。
25:02
好,你看到了啊,这就是那个M对象打印出来的。它的内容OK。它的类型是吧,后面是它的hash code OK打印出来了。然后呢,我们经过一次垃圾回收c.DC。11秒钟,让我们的GC来干活儿,干完活儿之后呢,我们再来去get这个引用结果,你会发现它的输出是这样子的。他执行了finalize方法。与此同时。这已经找不着了,就是说这个对象找不着了,M的对象没了。这就是弱引用它的特点,它特点也就呼之欲出了,它特点是这样的。在里面我们有一个M对象,然后有个弱弱的引用指向它。然后只要是垃圾回收器发现一个对象,发现这个对象了。他发现这对象呢,只有一个弱弱的风筝线牵着他。
26:01
直线啊,不是铁丝,不是硬的。啊,甚至也不是绳子不是软的。一根直线拉着他。直接把对象干掉。大家注意啊,如果说这个时候同时有另外一个引用指向它的时候,一个强用指向它,这个肯定是不能干掉的,听懂了吗?所以说这个M就变成了更加弱的一个引用了,就是原来咱们这个软引用是这样的,有一个对象被软的指着。啊,只要我空间够使我垃圾回收器不会把你干掉,对不对,可是这个弱引用的是。只要是我,被我发现了你,我管你空间够使不够使,直接将你干掉,好,这个结论就出来了。好。这就是弱引用的它的特点来特点,听明白了,给老师扣一,我们下面就来看看它用在什么地方。嗯,这就比较好玩了,对不对,你说一个对象吧,你弄个弱,你用指向他,反正只要垃圾回收器发现了就把它干掉,发现就把它干掉,你这玩意儿有啥用啊。
27:11
屁用没有是不是?但是呢,他跟某一个大家伙经常用的东西啊,这块初学者会听着稍微费点劲,只能尝试听吧,好吧,后面我也没办法太照顾你们啊,不然我这课没法讲了,听我说下面这个东西,呃,和它的作用就比较大了,和他联系就比较大,这东西呢,叫做thread local,嗯。看这里负logo。M等于new local是吧?好,我们来,首先我先问大家一句啊,有多少人是了解过three local的?给老师扣个一,这个应该不少,嗯。那如果有人在面试中被问到过的,给老师扣个二出来好不好?
28:03
面试中你被问到过这个相关的问题,嗯,还挺多的,对。这个local啊,我首先给大家,嗯,带个带了入个门啊,这个local到底什么意思。其实呢,你看我今天给大家讲的内容呢,是来自于我讲的一门课呢,叫做多线程。并发编程啊,就多线程的并发编程GC,嗯,Con currentrrent嘛,并发编程也就是说呢,它和线程呢,是息息相关的,这个thread线程本身是吧,Local OK,然后呢,这个thread local对象里面呢,有一个M对象。等于你有一个local,其实你看啊,我往这个local里面set了一个M对象,这看上去这个local的你的第一个第一感觉。实际上,它像是个容器。就是你可以往里头装东西,你发现没有提点set对不对。我们把这个TL里面set了个东西,然后你就set进去了,这个就是的第一感觉它是个容器。
29:05
这是没有问题的。好,那这个lo这个容器里面装了个什么东西呢?装了个M对象。OK。比如我们第一印象是local是个容器,这是第一印象。这个容器里面,我们可以往往里头装对象。这应该没有问题,比较简单。然后。比较好玩的是另外一点,大家注意看。大家看这里啊。嗯,这个person对象也比较简单,就是这么一个简单的person对象啊,Studyad class person person里面有个,呃。名字name属性的叫张三啊,然后构构建它方法的时候呢,把这个名字传进去就行了啊,默认是张三,大家看这里啊,在这呢,我们定义了自己的一个对象。Fair local TL等于newre local,这个local里面呢,我们指定它只能装person对象,这是没有问题的,你可以把它想象成为一个数组。
30:09
一个容器是吧,就这么一个容器,不是数组,这里面能装东西,装什么东西啊,装person对象。当然,这里装的是person对象的引用,它指向的是真正的person对象啊,大概是这样的。OK。接下来呢,比较好玩的点呢,呃,在这里啊。嗯。好的,瑞克。在这里呢,我们有两个线程,第一个线程。溜了一个thread。然后我设一秒钟之后呢,往里头设一个person对象,名字是张三。你可以想象一下,正常的对于一个正常人的理解呢?大概是这样子的吗?看这里,这里头我们有一个对象,这个对象什么对象啊,是TL对象是不是只有一个嘛,TL。
31:05
它像是个容器,好,它里面装了个person对象。装了一个对象。然后,当然这是个引用,指向了一个真正的。六出来的person对象,Person。大概理解就行了。然后接下来呢,我提点get,我拿到这个person对象把打印出来,这个没有问题啊,然后我另外一个线程。这另外一个线程是这样的啊,他先睡两秒钟,上面设一秒钟之后设,设了个东西进去没问题吧,往这个TR里面设了东西进去,然后呢,我第二第二个线程是。睡了两秒钟,然后我也去读这个提对象。哎,同学们,你们说他会读到什么呢?再再分析一遍。稍微照顾一点入入门级的,尤其是不知道什么叫local的,展开这了啊,我们的TR对象是一个单独的单单一的对象,好吧,往这看。
32:05
比如说我们这个TL啊,它拗了个对象出来,是我们。所谓logo对象这里面呢,我画简单一点,装了一个person,他名字叫什么呀,叫张三。好。这是我第一个线程干的事儿,往里头射进去了,我第二个线程等他射完了。两秒钟之后,我去这个里面去读东西,按照正常的理解,我应该是能够把这张三读出来的,是不是啊,同学们。我正常的理解对吧,你往个容器里,往个麻袋里,哎,给我装了个妃子,一秒钟你装进去了两秒钟,我读的时候是能把它读出来。可是我们跑一下,我们看看能不能读的出来。哎,你会发现线它读出来是空值啊,只要我装进去那个线程能把它读出来,我另外一个线程读的时候就读不出来了。
33:04
哎,这个这这这事就不对了,你明明是同一个容器啊,怎么你能读出来,我就读不出来。说什么?好了,这就是local的特点。那logo特点是什么呢?Logo特点是这样的,只有。自己设进去的这个线程自己能读出来。那么其他的线程是读不出来的。听明白了吧。就是相当于呢,它是现成自己私有的一个容器。好,这就是thread local的特点,好,刚我讲到这儿呢,Th th local本身是个什么东西,我觉得你就大致听明白了,我一会儿会给大家讲它内部是怎么实现的。就讲到这儿,你至少应该明白一点。对于local的访问。每个县城是自己私有的。
34:00
另外一个线程是访问不到其他线程的local的。这是第一个特点。好,下面我们先讲清楚这东西到底有什么用。然后呢,我们再来聊它背后的原理是什么,好不好?这开发过程会正常遇到吗?面试会经常遇到开发过程,其实你自己写死宅的可能性比较小。但是有同学肯定是写过的。和map有啥区别,都是存存内存,如果你把这里改成的是map。那么你在一个线程写进去,另外一个线程一定能读得到,你信吗?再去做实验好吧?嗯,好了,同学们,下面我们来聊呢,这个local到底有什么作用?这个作用呢?我先给你讲个比较简单的应用啊,我举个小小最简单的例子好吧,呃。
35:00
比如说啊,我们有一个特别深层的调用,这个深层调用是长什么样呢?比方说我在某一个线程里,我调了个方法A。然后这个方法呢,调用了B。这个B呢,调用了C。这个C呢,调用了D,这D调用了E,总之它调用的层次特别特别深,是吧,同学们。调用特别深,如果我有一个参数要传递的话。那我可能每次调用都得传递一遍,传给他,传给他,传给他,传给他,我想偷个懒。我就可以把这个参数装到我现成本地的t local里面,我把它装进去之后,比方说这个参数是person。然后只要是在同一个线程里,不管你是BCDE,哎,我全能读得到。我就懒得每次都传这个参数了。当然,这不是它的主要应用。好,听我说。
36:02
呃,聊主要应用。就要聊到那个对于框架的使用了。这个框架用过的同学给老师扣1SPRING。这应该用过是吧,嗯,给老扣这个常用的啊。这个不会,Spring应该是找工作找搞不定的内容,嗯,对,Spring框架呢,两个特点,一个是IOC,一个是IP。并不那么熟悉啊。但是对于spring的运用,不知道有多少同学用过这个,这个注解的叫at transactional。Transactional这块初学者们听不懂呢,你大致听就行了啊,And transactional这个指的什么意思呢?用这个六点来修饰某一个方法,比方这个小,这个方法叫M方法。好,这个方法的,呃,自动就支持了事物的处理。
37:01
自动都支持了事物的处理,OK。比如说它自动的帮你这里面你自己的一个connection,对于数据库的连接是吧,哎,注注解放进去之后给它注入,注入进去之后呢,M方法里只要用到这个connection了,好,它自动会帮你做什么set auto commit是吧,然后再呃这个这个事物的commit,自动的帮你进行事物的处理,那这件事情本质上是怎么完成的呢?如果你读。SP的源码。那么你就会发现这里面就用到了re local的运用,它为什么会用到re local呢?这里都会牵扯到呢,我们实际开发之中啊,我们这个connection是怎么用的,在这儿呢?我给大家举个呃,相对简单理解的例子,比如说。我们在自己的service里面或者controller里面,我们给这个方法做了一个注解,这个方法呢,小M,然后。
38:05
这情方法所在那个类啊,它一定有一个注入。把这个connection给注入进来是吧,同学们,呃,把这connection注入进来之后呢,在M方法里直接去用它。用它的时候呢,自动就支持了事物,我们假设我们的这个controller方法,这个M方法,它调用了A方法和B方法两个,A是从数据库里某一张表里面去读,B是从数据库里某一张表里面去写。好吧,假设M方法里面调用了这俩,那么就意味着这个connection呢会在AB之间做传递是吧?同学们,但是你们认真写的时候,你会发现这connection是猪肉的。他不会在中间做传递,但是你只要写transactional,它就全部支持了。呃,事事物的处理,可这件事情是比较神奇的,同学们,你们想一下啊。我们我们这个connection是从哪读过来的?
39:02
是不是从一个数据库连接池里啊,从数据库连接池里取了一个,然后这个A方法所在的那个类呢,它自己也有一个connection。那他也应该是从数据库连接值取的,那么B里面呢,它也自己有一个connection数据库连接嘛,那也是从这连接是连接值取的,如果这里面这个connection对象啊。他俩不是同一个,或者他们三个不是同一个。那这个还能支持事务吗?这不可能啊。哥们儿,咱俩咱们连连接方式都不一样,你跟我都关联不上,我都不知道你是谁对不对,对数据库来说OK。他们能支持事务吗?他肯定就不支持了,所以呢,如果你想支持这个注解transactional啊,注解是事物是吧。如果你想支持这个注解式的事物怎么办?你必须要保证这个connection。
40:00
以及它后面传递的方法里面用到的connection必须是同一个。必须是最开始从数据库连接池里拿到的这一个,而不能后面不能随便取。哎,那大家想到了什么吗?是不是这个connection放在什么地方最合适啊,兄弟们。放哪儿啊?放到县城本地是最合适的。发现没有?所以当你去读spring源码对于transactional的处理,你一定会读到local。这里面这个connection就是装在了local里面。呃,这个应用就稍微高端一点啊,刚入门同学没听懂就算了好不好,因为这里面牵扯的知识面有点多了。但是我觉得日常开发你们有过。半年的开发经验的都应该能听得懂。这个大家听明白了吧,嗯。
41:00
就这个connection呢,你一定保证同一个,那放在哪最合适,一定是放在同一个线程,你放心这M方法调用后面方法的时候,它一定是在同一个线程,它不可能跨线程的。可以装thread map map什么鬼,嗯。好,那么这个local的作用呢,大家就讲清楚了。如果面试官问你说用在什么地方,你最好用那个来做解释,这是面试官最想听到的答案。好,同学们可以继续的,同学老师扣一不同线程connection不同线程connection不可能放在一个单机事物里。但是可以放在分布式事物,分布事物是另外一个话题,那是有四五个解决方案,同时有一个落地框架呢,它叫西塔。那个是要,呃讲分布式的好吧。嗯。同一connection的副本,No no no nono,不要不要叫同一个connection的副本啊,严格来讲呢,它一定是叫同一个connection。
42:09
听懂了吗?就是你这个connection对象啊。看这里啊,你拿到这个connection对象。在你最开始的方法里面。你是由于引用指向它,在A方法里也有引用指向它,B里那边也有引用指向它,但是他们三个是不同的引用,这个没有问题,但是他们三个的风筝线指向的是同一个风筝,就是同一个connection,而不是他的副本,副本就意味着复制你得拷贝一份出来,没有没有,没有拷贝就是同一个。嗯。好了好了好。对对对对对。我看刚才有同学说不讲弱也用了吗?瑞用的。
43:00
咱们的那个特点我已经讲完了,你还记得吗?热用特点是什么呀?弱用特点是只要GC发现了它,它没有其他的什么强的引用啊,什么软的引用,指着他的时候直接就把它干掉了。那有同学可能会说,那这个论引用跟look有关系吗?是的,是有关系的,这个才是面试题里面比较难的地方。他要问你,他说这个local里面它是有内存泄露的,你了不了解。这这个。要聊要聊这个这个话题呢,就是说首先要了解什么叫内存泄露啊。什么叫内存泄露好不好,那那个关于内存的处理呢,一个叫内存溢出,最常见的叫内存溢出。还有一个呢,叫内存泄露啊呃,照顾一下初学者,什么叫内存的溢出。这就这么大地儿,我内存就这么大,往里头装对象一个一个往里装,装满了已经满了,现在又有一个对象,大对象要往里装,已经找不着空间了,怎么办?报错,内存溢出,Out。
44:12
Of memory。商量了,叫我内存不够不够使了,说白了这个叫内存的溢出。好,第二种呢,叫内存的泄露,什么叫内存泄露呢。指的是我内存还够使。但是。我里边有个对象。我在业务逻辑上我已经不用它了,我不用了,但这个对象呢,总是不能被回收掉,为什么?因为我有一个不太使用的强引用指他。然后我垃圾归收器呢,就不能认为他已经不用了,但我业务逻辑上已经不用了。那么这块空间呢,就意味着我后面的程序也用不了它,它也不能被干掉,它就像一个被遗漏掉的空洞一样存在这里,就这块儿空间被污染了,别人也占不了这个。
45:05
就叫泄露啊,占着茅坑不拉屎,对。但是泄露和溢出他们有之间有有有关联嘛,其实有一定的关联,比方说你泄露呢,如果你只有一小块儿,你就你就这么一小块,一两个地儿,其实关系也不太大,只要其他地儿够使就行,可是如果你的泄露越来越多,你有好多泄漏,这儿漏了,这也漏了,漏的漏的这个洞哪都是,是吧。那就会影响着这个内存会慢慢的全部被占满。那么任何对象再往里头找空间的时候都找不着了,它就会导导致溢出。好,这块大家听听明白了吧,嗯。能get到的老师可以啊。嗯嗯。对。所所以我们。我们我们我们讲local是什么问题呢?它是央视内存泄露的问题啊,它不是说会马上会导致内存溢出,但是它时间长了有可能会导致内存溢出,好我们下面下面来聊呢,这个外local。
46:06
和弱引用和内存溢出,它们之间有什么关联?好了。No。我们现在来看看到底有什么关联,要想关联的,要想理解这件事的话呢,我们一定要一定要去阅读local的源码啊,这也是。呃,现在有些。企业招人的时候,最愿意听到的是,哎,我我又了解原理,OK,就以前咱们企业招人呢,我招厨师。你只要会切菜。会做菜,会炒菜就OK了,后来厨师呢,太多了,我怎么去区分这些厨师哪个是优秀,哪个不优秀呢?都会做,你说宫保鸡丁大家都会做怎么办?哎,看你了不了解原理。你知道这个铁锅的材料是什么吗?它锻造过程什么样?对于炒菜有什么影响?
47:00
这个鸡是怎么养成的?有没有抗生素的遗留,鸡蛋还能不能吃等等。嗯,这就是内卷的产生,好吧,简单的呢,大家都会了,怎么办?问难的好。下面我们来聊local的源码,那么刚才我说过它,你可以把它看成一个现成本地的容器,这就比较好玩了,那它本质上它是怎么实现这个re local的呢?我们看怎么去读它啊,往这儿看,我们直接去看这个set方法就好了,Thre local本现的set方法,好吧,你点这个吧,For人local的set方法啊。好,我们点进去。我们要去看它的源宝了,走,你这样的,我们就进入了这个类。它的源码local类的源码,然后这是它的set方法,好,我先给大家呢,呃,十秒钟时间啊,你先从头到尾先把它读一遍。
48:01
这叫内卷的产生,对,十秒钟你把它读完。我先把它解释一下,它它是怎么实现。看上去只有一个容器,但是呢,它是怎么实现在不同线程之间不会共享的,好吧。嗯。读完了吧,应该。往这看啊,这比较好玩的是你看啊,往这个里面以线程为K,你扯淡啊。不要,不要误导。不了解,最好听我讲完再往再往外说东西,你要面试官问你。你跟他,你跟他说以现成为key,直接啪啪两个大耳光子给你扇出去。往这儿看。这个thread呢?呃,首先呢,它是要拿到当前线程,看到了吗?Thread current thread。就是你从哪个线程set的,它就会拿到这个线当前线程对象,这就跟当前线程产生关联了。
49:03
好,接下来。你会发现呢,这里面确实有一个map,包括它什么类型的,它肯定是个map get map t,我把当前线程作为参数传进去,它就会拿到这个map,实际当中set任何东西的时候,是往这个map里去set,好往这个map里去set,我们首先看看这map怎么拿到的,这是第一件事,好吧。好,我们来看看这个map怎么拿到的,走你。结果你会发现。还记得吗?就传进来的参数是什么呀。当前县城。然后呢,他去当前县城里面去找。某个成员变量。Three locals这个成员变量角,好,我们点进去看,这里又找到了我们thread这个类,也就是任何一个线程。只要他是个现成对象。你就会发现这个线程对象里面居然有个ma。
50:02
看了吗?也就是说,当你溜出来任何一个线程的时候,拗了个线程,拗了个线程,拗了个线程,在它内部一定有一个什么东西啊,有个map。这个map一定是key key value key value key value。这个线程有自己的key value key value key value,所以你就会发现,实际上往往里头set的时候,每个线程set的是自己的这个map set的自己的map set自己的map,那你这个线程能拿能拿他的吗?那1万%拿拿不了。好。这块大家能跟上看的看的明白的,我再稍微稍微过一下啊往这看,你看我们每一个RI local里往里头set的时候是怎么set的吗?它首先拿到当前对象,当前线程,然后呢,拿到当前线程自己的map,然后往里set东西的。OK。
51:01
我们第一个要讲的点就就就到这儿。可以对晒自己的map,那肯定互相之间不会影响。所以你自己装的东西装的装的那个connection啊,你肯定装自己县城里边了,好这块能get到的,老师扣一,可以继续的给老师扣个一好吧。嗯,对对。嗯,那好,那我们有了。为啥要用map,用list不就可以了吗?用list你get的时候,你你往里头装了很多个,你拿什么作为引用作为key呢,对不对。是不是一个map就够了,对呀,它只有一个map呀,没有好多个呀。好,下面呢,我们我们来聊呢,就是这个map里面装的key和value到底是个啥。来聊这个啊,Key和value map里面装的是key和value嘛,对不对,Key是个啥,Value是个啥?我们来读一下好不好?为啥简直显成堵阻塞呢?你也是神人啊,我现在给你讲馒头怎么蒸,你非得告诉我厕所怎么上,你非得问我厕所怎么上?
52:09
先听馒头怎么怎么蒸怎么吃,好吧,再聊上厕所的问题。来看这里啊,我们往T里塞东西。Set了一个什么东西呢?注意为我key level,我set东西的时候,我居然没有指定key,你发现没有,你说我给他塞东西,直接就set一个对象过去,我我我说我说key是什么了吗。我没说。好,我们看这K到底是个什么东西。Said,过去。找到map。Map,哎,这K是谁啊?The key叫this。大哥,哪位大哥告诉我,This是谁?你给我好好读。怎么还有好多人说当前县城啊?是当前对象,当前对象是哪个对象?
53:02
你看他累的名字,大哥。各位。这类的名字叫local,你居然跟我说这个this是当前现场,你你你在搞笑吗?谁掉就是谁,不扯淡,什么叫谁掉就是谁。哪个调的,我没有方法锁在那个那那那个那个那个那个调的对不对。真是能把老头气死啊,这老嘿。你不要告诉我,你你看到这个类的名字叫local,你告诉我this是别的是别的类的对象,你不扯淡吗?这一定是对象,可以类吗?这次一定是对象啊,你你你先把咱们那个入门级的课好不好,到咱网站注册一下,入门级课应该是免费的啊,那个好好听一听去。嗯。好,看这里啊。所以这个呢,叫做this this是谁?This是local对象。
54:02
是local对象,那这个local对象是谁呀,兄弟们。The刘总是谁?我,我,我们回回过去看一眼是谁?就这个这个this到底是个谁,你会发现调用谁的set方法。调用的是TL的方法。是TL的set方法,那这个K是不是就呼之欲出了?这T是谁啊?是TL本身。所以我们再再理解一下这个set的过程啊,分析一下好不好。首先呢。看这里。首先我们拿到当前线程对不对。当前线程是谁嘞?就是这个线程就是它是吧。然后当前线程呢,这个对象它关联了一个map,好,这个map里面可以装key和value,这value是谁?很简单,它一定是那个person。
55:04
所以这个value指向的是那个person对象,这个没问题。好,关键是这个K是谁,K是调用了TL本身,所以这个key指向的是谁啊,兄弟们,指向的是上面那个大家看不到的那个TL对象。那个local对象。所以还有人说这个K是当前线程吗?没问题吧,那有同学说我讲到这啊,我问大家一个问题啊,讲到这问问大家一个问题,如果我这个map里面我想装多个TL对象怎么办?多个怎么办?怎么办,告诉我。Map肯定是可以装好多个呀,就是你再来一个local,听懂了吗?调一。条二、条三。第四。OK。创建多个就行了,嗯。
56:01
好嘞。好,这个赛的过程啊,大家听懂的同学,老师扣个一来没问题吧,嗯。好了,理解这件事儿之后,我们再看这张图。大家看看是不是了解了。这赛过程到底长什么样?赛过程就是这样的啊,嗯,我们首先溜了个。Local对象TL指向了这个local对象,然后呢,找到当前线程的关联的map,这个key呢,实际上是这个local对象本身这个value呀。Value是谁?是那个person也行,是那个十兆也行,无所谓。好,讲到这里,有同学说,那和弱引用有毛有半毛钱关系吗?看这里。我们下面就来看看整个这个set的过程是怎么set进去的。
57:04
好,前面的可以继续,能跟老师跟得上的,给老师扣一,你有问题的你就直接给我提,我看大家伙就这块儿,我刚才讲完的东西还有没有问题啊。OKOK,还有吗?嗯嗯。为啥不用has map map,我给你解释完这个,你就会告诉我为啥不用tree map。是吧?我跟你解释为啥不用map,你说你你你你一定会问为啥不用其他的map。别这么多废话,先先把这个当前了解了再说啊。来,往这儿看。你有多个local是什么意思,兄弟你多多多项不理解什意思啊,你local里面要多装装好多个,你就在这里再弄一个LOCAL1T1。这个这个还用我解释,你再来一个跳一。
58:05
然后你要需要设多个多个多个value流进去,你就往TR设一个,TR1设一个,听懂了吗?你需要传多个多个值呗,在你线程本地保留多个值不就OK了。怎么还有人问这么这么恶心的问题?你你再问这种问题,我都我都来,哪位同学帮我怼他一下,同一个斯维洛,其他县城拿的时候不就拿重复了。音乐获取,那map是独有的吗?真是没搞懂。其他县城能拿还叫来吗?累死我了。啊,这种问题就不回答了,来继续啊。把老头给气死了是吧,嗯。开个玩笑,早就不生这种气了,嗯。
59:01
因为公开课嘛。以前的课你很多没听,所以公开课我我从中间领你一段来,你你讲不懂听不懂也是正常的,嗯。气死老头。好,我们我们继续来聊啊,我们来具体再来看这个set过程到底是怎么set。看这里挑点set。走,你。然后呢,往这个map里去set,它叫this value,记住这个this是条本身,Value是那个对象,Map set我们点进去。找你。好,这个set里面呢。呃,我给大家忽略这个其他的细节,细节源码的细节啊,我们主要看这个set是怎么把这个key,也就是local对象和value。怎么装进去的这里面呢,它主要拗了一个entry出来。Entry entry呢,如果你读过呃各种map,学过各种map,你就知道这个entry什么意思了啊,Entry就是一个K为对,就是就一条记录。
60:07
一个key一个value,这俩合起来叫一个entry啊,这我就给它忽略了,比较简单,好,它它把这个key和value装到了一个entry里。我们主要看这个entry是怎么装进去的,好吧。好,我走你。Entry。啊,我们主要看那个N垂的定义。这个N垂定义注意看。Steady class entry。注意看它的父类,父类是谁。它的父类是weak reference是弱引用。哎,这就比较好玩儿了。这个en居然继承了弱引用对象,这是在在搞啥?好,接下来我们看它的N构造方法。分别构造方法呢?把key传进去,把V传进去,注意看这里调用了super key。
61:05
Super什么鬼?当然是调用了它的父类的构造方法是吧?他父类是谁呀?它的父类是弱。引用兄弟们。Okay。他练对负类是弱引用,那是不是就相当于我调那个key的时候,我就相当于是拗了一个。Weak reference,然后把这key传进去了,有印象吧?那这就比较好玩了,兄弟们。也就相当于我这个K指向的对象呢,是被被谁被一个弱弱的引用指向的啊,可是这个K指向对象是谁啊,你还记得吗?是不是那个TL本身啊。也就是说。我刚开始溜出来那个TL本身。除了。有一个强引用指向它之外,还有一个弱引用的K也指向它,也就是说它两根线。
62:04
啊,一根铁丝。一根棉线。好。我们再从头到尾的捋他一遍。看大家什么能跟上啊,注意看。我们TL网里头set set设对象的时候怎么设的,是不是设的时候是这样的,首先拿到当前线程。拿到当前线程关联的map。然后呢,往这个map里塞东西的时候呢。把题号设为key。弄了个entry出来,而这个entry本身他爹是weak reference又调用了他爹的构造方法。也就是说把这个key的引用给它设成了弱引用。好,那回头看这张图。是不是能看懂了。呃,基本上所在呢,这张图你要记在脑子里。在脑子里之后,你就比较容易读懂它到底什么意思了。
63:03
回顾一下啊,就是我们代码里面怎么写的,我们首先呢,是new了一个TL对象,这一定是个强引用。指向了O对象对不对?然后呢,往里头塞的时候是拿到当前现成的map。找到这个map里面,注意这个key这个时候指向它的时候,指向的是什么的,什么引用呢?弱引用是个弱弱的。也就意味着这个对象本身被一个强引用指着,被一个弱引用指着。好。讲到这里能跟得上呢,该到这个特点的,给老师扣个一来。嗯。好,讲到这儿呢。当然很多人就该问了,为啥呀,对不对。我费这么大劲,我直接有一个前给指着他不就完了吗?我还要什么en从什么什么从从从从reference继承,我费这劲干嘛?
64:00
我用个前用纸样的行不行,可以吗。好,这就是local如果用强应用,它会产生内存泄露的根源。我们仔细来分析一下,认真听。如果这个引用是个强引用。嗯,还记得一个对象被回收的时候。只要有任何的强应用指着它,它是不会被回收的,对不对。兄弟们,我们列出一个对象来的时候,溜了一个对象出来。如果我想让这个对象被回收掉,我怎么办?M等于6M,我想让这个对象在垃圾回收的时候被回收掉,我怎么办?怎么办呀?哎,我就把M设为空就可以了,M设为空就意味着什么呢?这个也就没有了。没有任何引用指向这个对象了,是不是啊,同学们,它就会被回收,好,那我们翻过头来,我们看,我们看看这个问题。
65:02
我现在没有一个的对象。如果这个对象我不用了。这对象我不用了,不再使用了。那我是不是把TL设为空正常,在我理解的范围之内,它就应该被干掉,因为他不用了嘛,是不是?可是很不幸的是。假如。你里面设了个东西。如果这是一根墙,也用指着它,你只把这个引用设为空。这块能被回收吗?这块内存。它回收不了。是不是回收不了?再分析一遍。就是一个墙也用。如果我不想让我在在我的代码里面我写了,我把它扭出来了,我不想用了,不想用了怎么办?就这个引用,我不想用了怎么办?把提手一空就可以了,这个引用就没有了。但我理解没人指向他了,它应该被回收了,可是很不幸的是,你往里头所有的东西,你这个key指向他的,如果是个强引用的话。
66:05
实际上还有一根引用纸,它它能被回收吗?No,回收不了。那是不是很容易产生内存泄露啊?舍得吗?所以。我们反过头来想,那如果这边引用是个。热饮用的。RA,用什么特点来着?兄弟们。如果你把。如果你把这边引用干掉了,就意味着这个对象。只有一个弱引用指着他。只有,然后DC只要看到只有录引用被指的对象,他会怎么办?直接干掉是不是就不会产生内存泄露了?这块大家能听懂吗?嗯。
67:01
好,我刚讲完的这部分。能盖到给老师扣个一来。嗯嗯,OK。所以这就是。若引用。它比较好玩儿的地方。现在是若影不是没有泄露风险吗?对他这个泄露呢,其实指的是上一个版本。哎,我看到同学已经问了,老师拿value怎么办呢?你把这个设为空了,然后呢,这个课被干掉了,他意味着K就等于空了,大哥,这K等于空了之后这value怎么办呀。你拿不着他了,对不对。这value指向的这个对象。你是不是也就拿不着了,这十兆内存怎么回收啊?注意,这可是个强应用啊。出来了。这完了。是不是也会产生泄露啊,兄弟们。
68:03
所以。对于local的使用,还有一条非常非常重要的原则。这原则是什么呢?就是当你不用了某一个local之后呢,一定要要调用一句话。这句话呢,就是它。就这个,这有点。Remove。那有没有默认什么东西呢?指的是把这个条那条记录从map里面删掉,好,这个大家听懂了吗?这样就整个的咱们的那个local所产生的各种各样的内存泄露就全部被解决了。好,我知道有人会问这个问题。嗯,这个跟我同名的兄弟是吧。
69:00
我来看看。所以有个问题想不明白啊,既然弱引用解决不了。解决不了value泄露对,那还有意义吗?反正这个remove强调不是一样。呃,是这样的。对于设计任何框架的人,一定要从各个犄角旮旯的地方。来。来怎么办呢?就是来。这个。去除掉任何一种细小的可能性啊,就是任何一个来使用我这个框架的人,哎,不管你怎么用都不会出产出生产产毛病才对,明白吗。所以在它内部的框架里头,能做多做一丝任何的检查的,他一定要做出来。OK。这样,就算你万一忘了remove我这个key,至少不会内存泄露。我这是不是比那个。Key和value都可能有泄露,你忘了remove之后要好一点啊。
70:01
这个写框架的人呢,他的人家那个那个心思细腻的地方,好吧,对双重保险啊。是的,就是你框架要想非常的强壮,你要考虑各种各样的问题,能加一层保险就要加一层保险。好吧。如果强烈引用同时存在,弱应用不会被回收吧?这不废话,你强硬在那等着呢啊。肯定的。继续在其他服logo有概率。清理KV,对,是的,嗯。若用解决key对remove解决value yes,嗯。好好,其实在这里面还有一个非常隐藏的细节。呃,你在跟面试官聊的时候呢,你把这部分再聊出来,那是最好了。同学们,我不知道大家有没有读过线程池的代码。线程池。
71:02
这线程池代码呢?呃,估计很多同学可能没有读过。西池代码呢,其实有一个,嗯,就是你你用你用线程池的时候。一定一定要注意一点。你从线程池里面取出来的任何的线程。用完了之后。再放回去的时候。一定要注意对re local。清除掉remove掉,就是把这个map都给它清零,你想想看是为什么。对,会把口设为空,是的,为什么?想想看为什么。就是你的线程用完了,给他放回去的时候。因为呢。否看上去不会产生内存泄露,但是如果它长时间的在内存里头待着。你每线程池里头,你每拿一个出来,你每拿一次出来,你都往这个线程的local里面,就是往这个map里面设一条local记录。
72:03
你想想看,咱们这个这个线程池的线程很有可能是七乘24小时,365天不间断的,时间长了,他也会把这map填满。能理解吗?还有最重要一点是你往这个线程里面设的这个map里面。Local里面设的任何的数据,后面再拿到同一个线程的人,都可以读到以前的数据。这样会很有问题。所以呢,从线程池里面读出的任何的线程,往回写的时候一定一定。把现成的map清空,把它remove掉。这块大家能理解吧,应该。所以当你回答这个问题的时候,你不仅回答了弱引用。你不仅回答了。内存泄露,你如果还能够把这个顺带的把这个线程池给咱面试官聊出来,那面试官会不会觉得你的知识面特别宽。
73:03
对。答面试问题请大家注意,不要跟你平时考试关联到一起,平时考试我们讲,我们答个60分,答个80分挺好,完美。但是答面试问题的目标是什么呢?展现你自己牛逼的一方面。你最好是答80分,答90分,答100分,答120分,120分怎么答,用这个话题扯出来其他话题。扯出来线程池,再把线程池给面试官讲一遍。OK。同样的,你就干掉了其他人,这就是内卷的根源,Remove还不够吗?需要清空map remove。如果你remove的比较。比较呃,到位也可以。但是即便你全部remove掉之后呢?它是不是还有一个空map在这里啊,空的map也是个对象,是不是也也也占空间啊,如果下个线程我拿来的时候,我实际上用不着这个map,这个map是不是也占个空间。
74:05
所以仅仅remove是不是可以就精确的remove也可以?啊,但是呢,你最好是把设为空是最好的。他空map本身都都占空间,你这个连连这点空间你都不给他。嗯。搞这也要手动释放,只要手动释放的地方很少。只要手动释放,释放的地方呢,只有direct buffer,还有呢,就weak reference这些,这些地很少啊,其他都是,呃,这些帮你管了。嗯。用L处理线程池,清空map,你要愿意也可以,你要写个框架也愿意也可以,嗯。Value也包装成热饮也可以嘛。好了,你问了个触及灵魂的问题,来,兄弟们,各位兄弟们来。我想问问有没有谁回答一下他这个问题,帮我怼他一下。
75:06
Whether也当成若用可以吗?你value一次GC,你把你的value干掉,你value是你你你为什么要把一个数据,我想问你,就一个一个,我们拿那个数据库连接池来举例子啊,就你一个数据库的连接,你把它放到那个里面。你为什么把它放进去啊?是不是后面我要把它拿出来用啊,是不是?哎,你你用个路也用指向他,你你搞啥呢,我说。然后呢,你后面要用的时候已经被GC把它干掉了,你还用个屁呀。你的业务数据能,能这么干吗?不能。相同的KY6强引用KY6相同,你这是map本身的那个那个什么的问题,你自己自己查map去,相同的KY会不会覆盖到以前呢。
76:01
肯定啊。T是不会重复的啊。嗯嗯。好,我们。呃,热用讲到这里可以吧,就若引用和死人local啊,我我觉得讲现在为止,你你你肯定这面试题应该就知道了啊,来看看这面试题材。战略00REMOVE执行怎么操作?你拿大腿给我想想remove执行什么操作?来,拿大大腿给我想一下。Set里面,Set是往这个k value里面设了一个,就是map里面设了一个KV,对remove呢,把这KV队干掉。除了map源码,觉得梦是好习惯,但是不是必须的,任何一个都不是必须的,你你要是你不释放它,你就扔在那让它泄露,都都都可以听懂了吗?
77:03
你说这种话呢,对于一个精确的成员来说,他就是不爱听的。你你最好不要跟你面试官说这种话,尤其对于大厂啊。是,是不是必须的,任何一个东西有什么必须的吗?100%必须的没有。你吃饭也不是必须的,你知道吗?只不过多了就会死,你老不吃就会死。你老是不,我就有可能会死。作为程序员,怎么能说出这种话来?你是一个不合格的程序员。气死老头儿了啊,又,又把老头儿气死了,气死几回了。不喝水会死啊,你一次不喝,两次不喝不会死啊。你两天不喝水,你试试,你死不了没事儿啊。嗯嗯。好,看题吧。
78:02
这种类型那几种啊。一共有四种。强软弱虚。强软弱我就给你讲完了是不是。那种引用类型特点是什么呀?强硬的就不说了,软用特点是什么?哎,内存只要够使就在里边待着,内存不够使把你干掉。用在什么地方缓存?那rain用呢?弱弱的,只要是只有一个弱引用指向你的时候,只有弱引用指向你,干掉你。都在什么地方?好,Logo是怎么做的呢?要理解找到当前线程的map。Map里面设了个key key是谁?Key是local本身,Value是你设的那个对象,那个数据。Key呢?指向的是一个弱弱的应用,为什么解决K的内存泄露问题?同时要remove remove,为什么要remove,解决value的内存泄露问题?
79:04
记得在你用线程池的时候,如果你用到了local,一定要把它清空再放回去,不然你的数据就泄露了。嗯。若用还有其他应用场景吗?有的是。再去查。在你的JDK里面,你自己敲就行了,有一个类叫week。Weak hash map吧,啊,Weak map。专门里面装装弱引用的map啊,自己去找。下面两节课讲什么呀,老头。你,你是在跟我说话吗?来,把他踢出去。至少应该叫老爷子。虚也用,不讲讲讲虚也用,这个不容不容易理解的地方就更多了。这个。
80:00
嗯。那不是老不死,老不死,老不死不是那个老姑娘吗?对吧。记得笑傲江湖里面有一个哥们儿叫老头子。他们家姑娘叫老姑娘啊,老不死姑娘,嗯。嗯嗯,好好,呃。需要听听虚引用,能给老师扣个一来。选用呢比较麻烦啊。面试被问到了吗?谁没事儿会问虚引用啊?这不是有毛病吗?学友能听懂就听,听不懂就算了啊,尝试讲一下好不好,嗯。呃,虚荣是个什么东西啊?首先我跟你说这个,先先说它的特点是什么,好不好。这特点呢,比较特殊啊,非常特殊的一个特点。
81:02
我们来看一个虚幻的引用,它的虚幻的引用就叫phantom reference phantom虚幻,这我先跟大家说结论,虚虚引营特点是什么啊?如果有一个对象,这个这个对象。被一个虚引用指的啊,这个虚我都没法画了,画点点吧,点点点点点点点点这么一个引用,指着一个虚引用,可有可无,非常虚幻,跟鬼魂似的引用。好。那么不仅。它最大的特点是你,你还记得吗?我们我我们前面什么,不管你是soft reference还是weak reference,只要它被回,没有被回收的时候。我们通过他的get方法是能拿到这对象的,训勇是这样的啊,训勇的全称呢,叫phantom reference。PR,好,如果是血用指向的对象,你通过get方法是拿不着的。
82:06
你连get都get不到的对象,也就是说你根本就不能用它,你用不着。你get不到他。你都找不着他。所以你用血用指向丸的对象。不是给我们业务人员用的。先说第一个结论,因为我们业务人员根本get不到它。好,我再说一遍。虚引用指向的对象不是给业务人员用的。好吧,这是第一个结论,你听清楚了,记住了。那虚荣指向对象是给谁用的呢?虚引用指向的对象是给垃圾回收线程用的。是给垃圾回收器用的。那大就用它来干什么呢?主要用来跟踪堆外内存。堆外内存。
83:04
好了,这里面又有主要是这个这个这个这个这个这个这个这个各种的名词太多了啊,为什么懒得跟你讲这个呢?就在于这儿。好,那我首先我们讲什么叫对外内存好不好。你认真听啊,这个也是一个。IO方面的知识点。好,听我说啊。那么什么叫对外内存呢?大家都知道一个JVM呢,我们启动的时候,它会启动一个堆。这个我里面分配了任何的对象,拗出来,对象扭出来,对象扭出来,对象都往这堆里扔,都是往这块大内存里扔。但是JVM本身实际上相对于操作系统。相对于我们的Windows,相对于我们的Linux。它也只是很普通一程序,它只是占了一大块内存而已,那么在这块大的内在,在在这块内存之外,还有其他的内存。给别人用的。
84:02
好给别人用的内存呢,指的就是堆外内存对外。这这个就是对外内存的定义,听懂了吧。好什么,听明白什么叫对外内存,那老师扣一嗯。嗯嗯。哎,这叫对外内存。对外内存什么意思呢?就是它不归GVM管。也就意味着不归我们垃圾回收器管,我们说垃圾回收器它回收的是哪块内存啊。是这块内存,比方这里面有个对象,没人指向它了,好,符合我回收条件了,我就把它干掉,这是没问题的。但是拉器呢,干不了这件事儿。那有同学说了,难道还有对象是在对外分配的吗?是的。这个对外内存呢,一个典型的。应用的案例呢,是这个案例。By buffer。
85:01
字节数组。这个在so编程里面,在so的编程里面,咱们两门课呢,叫的那个游戏架构师啊,就是写游戏后端的,呃,写游戏后端的同学呢,一般都是要使用sock编程的。在这soy编程里面。都会使用到8ER缓缓冲区就是我从网络上接收出来的数据,我不是说接一个字节就马上处理一个字节,而是我要把它放到一个池子里面,这个池子就是一个缓冲区,哎,把这个池子呢,填的差不多的时候,我一一块把这个数据拿出来用。拿出来拿出来,去去去访问好吧。OK,数据不对,嗯,可以这么理解,嗯,这是内存的缓冲区,缓冲区有两种,第一种缓冲区呢,是在对内部分配的。第二种缓冲区呢,就是这种叫allocate direct,就是在对外分配的。那同学说对外对内和对外有什么区别呀?
86:01
好,我们考虑一个。实际当中的场景,好,这是我们的游戏服务器。这是他的GVM内存。我们的堆内存,然后这是他的网卡。呃,我们任何的数据啊,实际上是从网络上读过来,读到网卡里是吧,同学们。好,这个网卡呢。他的数据会被我们的操作系统放到它的一个缓冲区里面,这个缓冲区在哪儿?注意这个操作系统内部自己管理的缓冲区,它不会给交给JVM的,那JVM想要这个数据拿来用,他怎么办?告诉我一下怎么办?这个时候要拷贝过来。拷贝过来用。把这数据拷贝过来。好,这个过程能听懂,同学老师可以。为啥游戏编程网络技术比较多?你说为啥你写单机版的就不用网络了。
87:00
嗯。好,那你们,那你们发现没有,这里面就产生了一个效率上的泄露。这效率上是有是是是有缺失的。效率缺失,缺失在什么地方呀?就是你本来呀这份数据。我要是呃,自己操作系统处理的时候,我不用不用Java来处理,我用我用C,我用C加加,我能是只能直接访问到这份数据的,能听懂吗。我,我不需要发生这次。这口碑是不用发生的。你个头啊。王,跟这是两回事啊,好,听我说。呃。所以呢,这为什呢,就是说你你你你你你写Java的人,我也不能忍受,说老子这个效率上缺失太多呀,对不对。所以这边就提供了一个什么呢?提供了一个访问里面提供了一个direct by buffer direct,就是我不要这次拷贝了。
88:04
我是可以用用一个引用,对内的引用直接指向一个对外内存,就把这个这个地儿这次拷贝给它干掉了,就不需要了。这叫zero copy0拷贝。这个词听说过的老师扣一来,Zero copy,零拷贝,嗯。OK。这样的话呢,是不是就产生了一个。问题。就是在我的。这VM内部堆内存里面,我实际上是去访问了对外的内存,假如我在对外分配了一个一块儿一块儿一块儿这个这个这个空间,这个空间呢是我的一个buffer,我用来接收网络数据用的。那么当我这个对象被干掉的时候,我是不是顺带的应该把这个。外面这个。
89:00
八分也得干掉。这这个大家能理解吗。嗯,我再说一遍。我再说一遍。你看啊。By buffer这个东西的对象,它一定是在JVM内部。但是我direct实际上它就指向了一个对外的内存。这样我就不用拷贝了,就零拷贝,那么如果这个对象被干掉,这个buff分对象被干掉。那么是不是顺带着他应该被干掉?听明白了吗?来这块能跟get到的,给老师扣个一。其他程序不会要用吗?要用你就别干掉啊。这个这个逻辑怎么这么费劲呢?你要用你别干掉他对吧,你不用的时候,你得把它干掉嘛。
90:00
听懂姥爷了是吧,嗯。这个办法引用是用户的用户太内存,内核太内存。没有用户太内存,还有内核儿,太内存没有这个啊。你说是那个程序管理的,还是内核管理的?这个应该是这个内核才会用到自己管理的时候才会管理啊,这就是用户管理的。不是不是内核啊,你想吧,你想吧,这个Java可以可以直接管理的,明白吗。这个无所谓啊,这两回事,你不不用不用纠结这件事儿,这个跟咱们讲的东西没关系啊。没有这种说法。啊。内核程序用到的那叫内核,内核的内存,嗯。好,往这看啊。呃,这块如果大家伙想听的特别明白啊,我推荐大家去听咱们周老师的艾欧的课,主要是艾欧课呢,会从那个呃内内内存的这种零拷贝,从Linux内核的对于IO的处理。
91:11
来给大家讲起这个就会比较非常清楚和和明确了啊。嗯。今天呢,没法讲那么深,因为它一个一个IO的内容讲了好长时间啊,这个也是非常容易被问到和容易混淆的一个难点。好,我们现在会回到这个管理对外内存这件事情上,也就意味着,呃,如果说某一个GVM对象,它管理的一个对外内存,那么这个对象被回收的时候。一定要把这件事儿给干掉,把外面这个内存也得干掉,那怎么才能干掉它呢?所以。就是拥有对外内存管理权的这个对象,在他被干掉的时候,一定要通知管理器,通知我们的GC,通知我们的GVM,做一个特殊处理,就这哥们儿太特殊了。
92:02
为什么?因为他管理了一个对外内存。听懂了吧,同学们。嗯。好,这块能get到的给老师扣一,所以呢,我们要探讨一种机制,什么机制呢?就是这种机制呢,让一个对象,一个特殊的对象被回收的时候,要通知到垃圾回收器,说这对象比较特殊,那怎么通知他呢?这里用到的。就是phantom reference引用。好,那实际当中怎么用的呢?实际当中是这么用的。如果我们要用一个一定和要有一个关联的队列。我们任何一个对象。装到一个fat reference的时候,一定要给他传一个队列进去。也就意味着呢。你可以理解为。这个对象是一个特殊对象,它会被它这个引用,会被装到一个特殊的队列里。
93:01
这reference本身啊,Reference这个这个虚引用本身被装到这个队列里,这里面装的全是虚引用,虚引用虚用虚引用虚用虚引用虚引用好,然后我们垃圾回收去,回收的时候一定要查看这个队列有没有东西。如果有东西。就要进行特殊处理,他发现它是一个管理的对外内存呢,就要顺带着把这个对外内存给干掉,不能够只回收对象本身。大概就是这个意思啊,考程序我就不演示了。和玄乎对,你们非得要听这个,肯定玄乎一点点。还有人问什么时候对象回收的,没法儿讲了,你这个最开始你没听,我也不可能从头再给你讲一遍。果断放弃,嗯。不建议你放弃啊,还是那句话,送送大家四个字。我建议大家,你呢,对自己不要要求太高了。
94:01
日工一族。每天前进一点点。有公式不是吗?1.01的三百六十五次方。三点多少还是多少来着,我忘了。0.99的三百六十五次方啊,你日退一组。0.0.0几了,就没了。每天呢,只要你保持让自己前进一点点,说现在大环境不好。各个厂子裁员,你有办法去改变它吗?你能让厂子不裁员吗?No。不行,那怎么办?改变你自己,让自己变得更强。只能这样。37.780.03ok,好。送大家这四个字啊,希望大家装在装在脑子里。OK,我今天的技术我们就讲到这儿好不好,但就是你你看上去它比较简单,你会发现呢,他牵扯的面儿实际上是非常宽啊。嗯。
95:01
稍微介绍一下我们课。然后今天如果大家伙儿对于工作上,我看刚才也有人问啊。那个工作上有什么疑问呢?也可以找老师来问,说要不要考研,考研考失败了怎么办?我想进大厂怎么玩?我今年工作三年了,我想多挣点儿钱,怎么办等等。嗯。简单介绍课程。今天给大家。讲的这部分课呢。嗯。是来自于咱们Java后端架构师这个简称MCAM呢,目前我们也跟那个,呃,工信部达成了。
我来说两句