00:00
那么我们这一章呢,叫做虚拟站啊,整个这一章呢,我们就算是告一段落了啊,那简单回顾一下啊,咱们这一章呢,其实主要讲的呢,就是这个站的这个内部结构,内部结构呢,我们说提到叫战帧,战帧呢具体又分为叫局部变量表,操作数站,动态链接和方法返回地址,当然了还有这个叫一些附加的信息了,整个呢,就构成咱们一个战争的内部的这个结构,比较重要的呢,叫做局部变量表和操作入站,顺便呢,咱们又讲一下这个方法调用的一些相关情况,这是比较重要的这个内容,诶同时的话呢,我们在讲解的过程当中,实际上呢,咱们对这个自解码指令呢,应该有更清晰的一个认识了。诶行,这呢,就是咱们说的这个这一章的一个主要内容,最后的话呢,咱们再看一下关于站这一块,在面试当中会涉及到的一些相关题目,哎,整体来说呢,其实不难,大家来看一下你呢,是不是能够很顺利的答出来。好,我们看一下这几个问题啊,说呢举例战一出的情况,哎,这个战一出的话呢,我们说是咱对应的一个error了,嗯,看你还是不是能够把这个error呢,全称能够说出来啊,叫stack overflow啊的一个error,对吧?哎,这是我们说的这个就是当我们这个占空间呢,一个一个的去加战帧不足的时候,最后呢,就导致这个站溢出的情况,我们在当时讲占的大小的时候,是吧,咱们设置这个站的大小呢,嗯,这个我就写到这吧。
01:26
这样子,嗯,咱们当时是说了,可以通过叫杠SXSS,哎来S设置占的大小。嗯,当然呢,我们说这个占空间啊,可以是固定的,也可以是这个动态变化的,那如果你要是固定的话呢,这个不足了,最后就出这个error了,如果它是可以动态变化的,我们设置好以后呢,后期后期还可以再让他去,比如说自动的去扩容,但如果要是整个内存空间呢,已经满了,他再去扩,扩不了了,当时我们还提到过相应的一个情况,就是叫OOM对吧,这个呢是当我们这个整个内存空间不足了,我们站在去扩容的时候,出现了这样的一个O的异常。
02:07
哎,那这问呢,溢出就指的是sta flow啊,后边呢,咱们还会提一个叫做,呃,这个叫占溢出啊,咱们整个在扩展呢,叫内存溢出,还有内存泄露的问题,咱们到后边的时候呢,再来讲这个问题啊行,这呢是说的这个溢出的问题,下边提到了说如果我们调整这个站的大小,能不能保证说就不出现溢出了呢?嗯,能吗?呃是有可能,但是他说就能保证不出现吗?这个是不能的,不能保证的对吧,咱们当然演示这个杠,呃XSS的时候,呃见过这个情况啊,就是比如说我们这个内存这个占空间的是这么大的,我们去运行像一些代码的时候呢,有可能它会执行比如说5000次。五千四呢,它就会报这个呃,Stack overflow了,那我们要是调整这个大小以后,让它变得更大一些,这呢,我换一个颜色,呃,比如说额外的我又加了一些这个占的空间大小,最后呢,它能够呃,运行多少7000次才会出现我们的stack flow。
03:11
那是这个情况啊,那现在的问题就是说,如果我们现在有一个代码,这个代码的话呢,它是一个比如说递归的一个调用方式啊,它需要呢,执行6000次能够正常的去结束。那这呢就会说,如果你要是用这个相对小的,这个红色的占的大小的话呢,它就会仍然报sta of flow,但是你要是那这个占空间稍微大一些,咱们用的能够让它跑七千四的这种情况呢,它就不会出现sta of flow,所以说呢,这个呢,我们是能保证,但是说如果你本身它这个是一个死循环一样啊,本身这个递归就不会有终止的情况,那你不管内存设置多大,它都会出现sta flow。啊,是这样的情况,就是咱们把这两个例子都举了,也就是说呢,咱们调整占大小,让它变得更大,理论上来讲呢,只能够是让我们的这个,比如说stack overflow呢,理论上来说哈,只能是让它出现的这个时间呢,更晚一些了,但是不能够确认它,确定它一定不出现,只能是这个样子。
04:17
对吧,只能是这样子啊,就是说呢,给你500块钱让你花,你可能花五天,你把这500块钱就花完了,然后现在呢,给你5000块钱呢,只能是保证呢,你能够多用几天,多用几天,但是你可能还会有一天会把这5000块钱给用完。啊,就是就是能让你多用几天,这个呢是能够保证的,但是说你会不会给你5000,你就这辈子就够用了呢,这个不能保证是吧,你说你拿着5000块钱,你创业了,创业了你你你可能是能让5000块钱升钱了,OK,你再也就不用不用再要钱了,但是多数情况下呢,是达不到这个点嘛,所以多数情况下呢,不能保证你不溢出了,不能保证你不缺钱了,只能是能能保证让你多用几天是吧,才能这个样子啊,这就我们说这个点。
05:01
行,那说到这个点的话呢,咱们顺便的就把这个点说一说,诶前面呢,这咱们提到了是说这个占空间啊,它会出现这个溢出的情况,那问一下它会出现溢出,我们能够通过垃圾回收去避免这种溢出情况吗?这就涉及到了垃圾回收,会涉及到我们的虚拟占空间吗?诶这要明确呢,是不会的。哎,这叫不会的,那说到这儿的话呢,其实咱们也讲过这个知识点了,回过来咱看一下。看一下这啊。看下这咱们其实前面呢说过啊,讲这个程序计数器的时候呢,提过这个问题,嗯,我这样来说,这呢叫error,呃,写全吧,这叫error,这个呢叫JC,好,我们看一下运行时数据区的这五部分,哪些有error,哪些有JC啊,我们按照这个讲的顺序说啊,像程序计数器咱们讲过了,程序计数计数器呢,是运行时数据区当中这个运传速度最快的是吧,就是存储我们这个指令的下一个地址的啊,它呢不存在error。
06:01
对于我们这个成熟计数器来讲,不存在说他也没有JC。啊,运量速度最快啊,空间也非常小啊,就存一个指定地址,然后呢,我们讲的这个虚拟基站,虚拟基站呢,是会存在error的sta flow error嘛,它呢不存在JC。它只有进站出站,通过出站的方式呢,你就可以理解成就像垃圾一样就出去了,直接出站就可以了啊,它用不着去显示的来回收了,所以它不存在,而我们这个本地方法站呢,虽然我们还没有讲,但是它也是个站嘛,区别呢就是我们这调的是Java的方法,呃,本地方法站呢,调的是咱们说C相关的一些这个方法了,所以说呢,这个本地方法站跟这个虚拟站呢,在我们house里边,其实它就是同一个啊,同一个就都是这个县城里边这个站了。嗯,它呢,也不存在这个跟跟我们这个虚拟基站一样啊,存在这error,但是不存在这个JC,然后方法区跟我们这个堆空间,咱们先会去讲堆堆呢也是内存当中比较大的一块区域,它是会存在error若的OM,对吧,空间成不下了OM了,那通常呢,在这个OM之前呢,我们都会有相应的这个JC啊,Manager j c major j c for j c这样几个概念,到时候呢,也是我们重点要讲的是针对于堆空间的垃圾回收,这是咱们的一个重心啊。然后方法区的话呢,它是放的时间比较长的一些数据,比如说我们这个类文件本类数据本身它的生命周期呢,通常是跟我们的这个虚拟机的生命周期是一样的,但是呢,也不排除方法区中我们加载的炸包过多了,导致方法区呢最后也溢出了,所以它在溢出之前呢,呃,首先它有溢出是吧,然后在它溢出之前呢,我们通常也会有four j c啊,所以说呢,方法区堆都有error,都有JC,哎,然后这个。
07:48
两个站呢,是这个样子的啊,然后程序计数器呢,是这样子的,彻底的把这个整透啊好回来,嗯,那我们该看这个问题了,说分配的占内存越大越好吗。
08:01
越大越好吗?给你的钱越多越好吗?你想诶挺好啊,越多越好啊,多多益善嘛,是吧,那越多的时候呢,会不会导致你会有一些习惯会变差呢,会不会有一些这个其他的事情发生呢?对吧,比如说这个这个咱们这个国民老公王思聪是吧,这个老王给了他应该不少钱去创业,让他去折腾,那你对咱们普通老百姓来讲,你说拿十几个亿想都不敢想的事是吧。都不知道十几个亿到底堆出来能有多大一块面积,这都没有这个概念哈,那不,王思聪照样得败没了是吧,然后好像还是被列入老赖了吧,那你给的钱多了又能怎样呢是吧?呃,那内存空间分配的这个占空间越大就越好吗?理论上来讲,你越大的话呢,我们就能够避免你这个出现staf flow的这个概率了嘛。呃,咱们说你出现的概率其实也不能这么讲吧,我们应该说在一定时间之内发生的概率降低了,对吧?因为呢,我延长了你可能发生的时间了,那么在单位时间之内呢,你发生的概率不就小了嘛,啊,应该这么讲哈,就跟咱们说,呃,现在我们这个增加人的寿命,我们说呢,能够减缓人这个让人活得更可以更久,是把这个时间拉长了,但是每个人最终目的一定会死,对吧,就是最终的结果呢不会变,但是呢,呃能拉长这个时间,呃分配的内存越多呢,我能拉长,呃避免你出现这个sta flow,这个出现的过早,但是呢,我可能。
09:32
避免不了你出现。那是这样的啊,那越多就越好嘛,那就是说对你自己来讲呢,可能要好一点,但是对别人可能就不太好了,对吧?地球上人太多的话呢,挤占其他生物的一些空间啊,比如说我们这个占空间,你这一个占变大了,你这一个占变大,整个我们这个内存空间也是有限的,一个占大了,这是一个县城,那这空间更大了,你是不是几占的,我们这个线程数可能会变小,对吧。哎,线程数会变少,甚至说挤占我们其他的内存空间啊,因为整个空间是有限的,地球上的整个资源是有限的。
10:07
啊,这个人其实想想地球的话呢,地球话其实也不是说就是我们人的,对吧,你看人把它分成一个国家,一个国家的边界,好像说呢,你在这个国家之内的这个生物就都归你所有,呃,完全人凌驾于其他生物之上,想想地球的话呢,有六六七十亿人口是吧。嗯,然后呢,人还处在生物链的顶端,下边这个生物呢,说实话还挺惨的啊,这个人你越多的话呢,你感觉挺好,但是确实挤占了其他生物的这个空间啊,跟我们这一样,你占呢,呃,这个大了以后呢,你自己出现异常的机会少了,当然呢,相应的你会挤占你自己其他的这个占堆的线上的空间,乃至于其他生物其他这个内存结构的空间,所以这个也很好理解啊,那当然了,我们这个就不是了呗。其实一般呢,大家答这些问题的时候呢,一般是越怎么着就越怎么着吗,或者说是一定会怎么怎么着嘛,肯定不会怎么怎么着嘛,一般这种问题呢,都是否定的。
11:06
对吧,一般都是否定的啊,就是一般这种说的很绝对的,这种很偏执的这种观点,观点啊,通常都是错误的。这个应该很好理解,就是像马克思讲的叫什么辩证唯物主义嘛,说任何一个事物都有好的,也有他坏的方面,就就这种观点,你想我们其实很难驳倒他啊,他这个就是不偏执的,一般都是对的啊,当然这个马克思也说了一个是绝对的,那就是呃,运动是绝对的。啊,但是他说了这句话以后呢,又后悔了哈,说呢也有相对静止的状态是吧?啊,这个昨天呢,你借了我100块钱,今天呢我找你要,你说呢,诶,这个今天的我已经不是昨天的我了啊,说你得找昨天的我去要钱啊,这就是夸大的这个运动的这种绝对性是吧?我们说呀,也有相对性质的哈,你还是你,你得还钱啊,还是说人不能同时这个踏入一条河两次啊,这其实也夸大了这个叫呃运动的这种绝对性。
12:02
啊,就是你看他说了是绝对的,他还往回搂是吧?呃可见呢,就是我们说的任何观点啊,呃,都不能太绝对了啊,像这种呢,一般说越怎么着越就怎么着,呃只要呢,你你你清楚的,当然就按照那个答案,你要是不清楚的情况下呢,一般你答否定的话呢,它这个正确的概率要高很多啊好,然后看下边最后一个问题,说方法当中定义的局部变量是否存在现场安全问题。方法当中定义的局部变量是否存在现场安全问题,这个问题的话呢,我们说得具体问题具体分析,诶把这个点呢,咱们拿出来看一下这个代码的情况。哎,这这我们说这要具体问题得具体分析啊,我已经写到这儿了,那么怎么来看这个事儿呢?我下边呢,是列了一个例子。那下边列了一个例子啊,我们看一下这大家可能觉得还挺复杂的哈,就是这个情况呢,我们可以稍微的多说一下。嗯,我这里边你看这个METHOD1,这是咱们的一个方法啊,那执行的话呢,它就是一个战帧了啊,然后在这里边呢,我定义了一个叫string builder,那注意你看我用的这个string builder呢,其实是有讲究的。
13:09
你想想有啥讲究呢?我为什么不用buff呢?诶,String buffer跟string的区别呢,就在于string buffer它已经是线制安全的了,String builder呢,它里边是线程不安全的是吧,那这个这个应该都清楚吧,CTR shift t string,我先看一下buffer,在这个string buffer里边呢,比如我们看它内部的这个方法的时候呢,你会发现它已经加上这个S同步了,而我们对这个string builder的话呢,点开它是没有加这些同步的。看它们二者的其实主要区别呢,就在这儿,它二者的这个副类呢,其实都是一个结构。啊,叫abstract string builder,你看这个string buffer。Ofstract string build对吧?嗯,它们二者的区别就是在于这个同步的这个事儿啊,方法呢,其实主体上都是一样的,那现在的话呢,我们说这个里边定义的安全不安全呢,你就不能拿一个已经安全的来说事儿了。
14:04
我这里边儿要用string buffer的话呢,那没得聊了,它里边已经给你处理安全了,你再弹它也是安全的,对吧?所以这块呢,我们刻意的得用一个本身它是不完全的一个结构啊,我用string builder的,哎,这个是有讲究的啊,那我在方法内部像这样的方式呢,定义了一个局部变量,呃,里边呢,做一些操作了啊,这个添加呀,反转呀,替换呀等等这些操作,那它安全吗?呃,应该说string builder不安全啊,但是我们在这个方法内部定义的这个S1这个变量呢,它是先常安全的。哎,那这里面就提到了,怎么叫现场安全是吧。何谓线程权,那就是说呃,如果只有一个线程哎才可以操作此数据,则B是这我一说B是这就又像是个绝对的话了啊,形程安全的啊,B是是因为我这就只有前面有个先定条件啊,只有一个线程的时候。
15:09
啊,这是一个情况啊,然后呢,呃,如果有多个线程。哎,多个线程操作此数据,哎则此数据,哎我们首先说呢,是共享数据。哎,如果不考虑同步机制的话。会存在。呃,线程安全问题,那那杨一志呢,就是如果你要是考虑的同步机制呢,它就不会存在线程安全问题了,那我们看这个问题这里边儿呢,我们是在方法内写的,这个方法呢,是被某一个线程所调用的主线程或其他线程啊,那因为这个数据呢,只在当前线程中才被调用,就属于我们的第一种情况。所以呢,我们这种情况呢,对应的咱们说的这个呃S1是吧,呃它呢,整体上来说哈,它的这种声明方式。
16:09
诶是线程安全的,哎,这个注意简单来讲呢,就是我们这是一个线程,如果我们还有其他线程的话,目前呢,咱们这个METHOD1就在比如说我们这个叫线程一。诶,这呢是其他的一个,比如说我们这叫线程二啊,我就在线程一当中调用了这个MASTER1了,MA1就放进来了,然后里边呢,我盯着个S1这个战帧,这个战帧呢,是归咱们这个线程一所有的,它不会被我们其他的线程所调用,所以呢,诶保证了它是只能被单线程所使用的,它就是安全的,那如果说呢,我们又存在着一个,比如说在两个线程之外定义了一个数据啊,它能访问,它也能访问,哎,那么此时呢,这个数据如果没有考虑同步,它就是不安全的。就是不完全的啊,比如说呢,就是咱们在这个里边举个例子啊,比如我定一个嗯,Int型的一个number,哎,是个十,然后我们在这个方法里边呢,去操作这个十,哎,然后我在这个这个方法,我这写成个静态方法了,这个METHOD1呢,比如咱们在这个主线上当中,我就调这一个METHOD1,先忽略其他代码啊,我这调METHOD1,然后你自己再去扭一个线程。
17:23
啊,你有个线程在这里边儿呢,我我我这就简写了。那在这里边儿呢,你再去,呃,调用咱们上边的这个。Number是吧?诶,那么在这个分线当中,你也去调number了,然后在这里边也去调这个number了,哎,这个number呢,就可能会存在一个线程的安全问题了。对吧?哎,这个大家要清楚啊,OK,嗯,当然这个呢,我们一是静态的,你这个要调它的话呢,静态里边不能调非静态,那你把这个加个static就行行,这个呢,因为大家都做过开发啊,所以这个我就不多说了,应该都很清楚。好,那第一个呢就过了,然后看第二个,第二的话呢,跟第一个区别就是我们把string builder呢,是作为参数呢传递进去的,哎,我是传递进去的,在里边呢做了一个加的运算,那此时的话呢,诶我们这个参数呢是这呃变量呢是针对它来讲的哈,它的操作过程,哎咱们说呢,是线程不安全的,就看一下,首先你这个位置呢,是从外边传进来的,它不光是归我们这个线程所有,是有可能是被多个线程所调用啊。
18:32
对吧?呃,一旦呢,被多个线程所调用,它本身呢,有没有考虑过安全机制,所以它存在着线程不安全的问题。嗯,你看我们这个针对METHOD2啊,大家看我这是没方法。啊,没方法,然后我们用了一个齿轮build。哎,我这个你写这也行,或者我我放这也一样啊。我在这个主线程当中啊,我去调用这个METHOD2了传进去,让它在里边呢做一些加的操作,然后呢,我又造了一个线程,然后这个线程呢,也去操作我们这个,呃,Build啊,那实际上是有可能,嗯,不是我这样写,我这样写不对,必须得放在这。
19:13
必须得放在这儿啊呃,我要上面写的话呢,它一定是让主线程先执行完了就啊,诶我放这儿,然后我们这块呢,往下走,你把这个分线程呢给启动起来了,然后主线程接着往后走,那么分线程呢,一个是添加主线程呢,也是一个对string build的一个操作,它俩呢,可能会这个所谓的我们叫呃这个去抢这个资源了是吧?呃这个并发的去执行了,先言后后去执行,可能交叉,那这个时候呢,我们说就有可能存在这个县程的不安全问题。哎,限制的不安全问题啊行,当然这个呢,你要严格上,严格上来讲,它不算是我们方法内定义变量了,哎,算是这个行参的变量啊行,然后第三个大家看。此时的话呢,我们是string builder,我在里边定义的,那么针对这个SE的操作。
20:06
是线程安全的吗?那这个我们其实还是是呃,线程不。很安全的。诶或者说呢,它是有可能存在线程不安全的问题。有同学说,诶这不是在内部定义呢,怎么就不安全呢,你看我这个是有返回的啊,我相当于把这个SE呢给它返回出去了,一返回出去呢,诶它就有可能被其他位置上的多个线程所调用。啊,你把这个呢给扔出去了,好几个人呢,过来抢一个人呢,是一个县城,这不都来操作就有可能不安全吗?诶,上边这个一呢,就是直接在内部用了啊,在内部还消亡了,没有被其他线程共享的可能性,所以它是安全的啊,那这个和这个都不行。哎,都不行啊,那大家你看我再这样写一下呢,我这个叫四。
21:01
这个呢,我改一下,我就叫做string,在这个位置呢,S一点,我调一个two string。那问这个里边的S1它的操作。这个操作呢,是现场选的吗。哎,我说是现场安全的。注意,你看我跟METHOD3呢是不一样的。这个呢是将string builder给返回了,而这个呢,我们针对于SE这个string build来讲,我调了一个to string方法SSE其实在内部消亡了。S死了就是在内部生的,但内姆死的跟我们第一种情况呢是类似的,所以呢,它仍然是安全的。那这个初率怎么理解呢?点开代码看一下。String build当中我们调的to string,相当于是我们重新又new了一个string,所以我们这块呢,又返回的这个string呢,可能会被多个线程所调用啊,是不安全的,但是我们的builder是安全的。
22:04
哎,这样子的啊,行,那总总结一下的话呢,大家发现了,就是我们如果这个内部的这个变量是在内部产生,内部消亡的,它就是安全的内,不是内部产生的。像这个不是内部产生的,或者说是内部产生以后呢,又给它,呃返回到外面了,呃生命周期没有结束,像这种都是不安全的。后面呢,咱们讲到这个堆的时候,最后要谈一个问题,说呢,我们创建对象都在堆控件中创建吗?实际上不一定,我们还可以在站上创建,那我们到时候会讲一个呢,叫做逃逸分析,那像我们的呃,这种情况,这种情况呢,他就都发生了,诶我看这种情况和我们上面这种情况就都发生了逃逸。是吧,都逃逸了,就是它这个作用域呢,不光是在我们这个方法内部了啊,就是对应的跟我们那个是一致的啊,那像这种没有发生逃逸的这种呢,它就是安全的。
23:03
哎,后边咱们还会再讲这个问题啊行,这儿呢,就是我们把这几个面试的问题呢讲一讲,希望对大家呢,关于这个站结构呢,有一个更清晰的认识,好,那整个呢,关于我们说的虚拟基站,咱们就告一段落。
我来说两句