00:00
前面啊,咱们把这个堆空间当中常用的,呃,虚拟机运行的参数呢,咱们给大家总结了一下,那实际上呢,总结完以后呢,基本上咱们关于这个堆空间的讲解啊,哎,就算是告一段落了,但是最后呢,这块呢,又给大家补充了一个内容啊,这个内容呢,咱们算是做一个拓展。哎,做一个拓展啊,我们来看一下这个问题呢,是说堆。是分配对象存储的唯一选择吗?这里边呢,其实默认了有一个情况,就是说我们new的对象啊,默认情况下呢,这个对象呢,是不是都是分配在这个堆空间中的,对吧?哎,那现在问题的就是说,我们是不是说堆空间是唯一的一个选择。啊,那这个结果呢,肯定说不是对吧,那如果这个结论呢,说是,那咱们也用不着呢去讲下边这些PPT了,好,那我们就来理解一下,看看这个概念呢如何去说明啊说堆是我们分配对象的唯一选择吗?哎这呢哎,为什么我们要考虑这个问题,主要呢,原因还是基于我们这个堆空间啊,比较特别。
01:00
哎,大家也知道这个堆空间是咱们这个JC的,是不是重点区域啊。啊,那同时呢,就是它也是影响我们性能提升的一个重点区域。啊,所以呢,我们呃,一说到这个性能瓶颈啊,其实主要呢,体现的就是我们这个堆空间。啊对,空间,那我们能考虑的事情啊,是不是就是说尽量呢,你是不是让这个JC出现的频率是不是要低一些,对吧?啊,那这个频率低的话呢,咱们主要呢,降低的是这个,哎,老年代的这个JC啊,因为新生代的mini j c啊,它毕竟呢速度还是挺快的,就STW的这个时间呢,还是比较短的啊,那我们在这个老年代的这个JC花的时间呢,哎,SW的这个时间要长一些,所以主要呢,我们要降低这个老年代的这个垃圾回收是吧,考虑这个性能的一个提升啊,包括呢,在堆空间我们也可能会出现oom,咱们通过这个参数的设置呢,哎,要避开这个OM的出现。啊,这是这样问题,那么咱们也知道了,说这个对象啊,主要呢,是不是都是分配在这个堆上的,那我们能考虑就是说,诶,除了我们提到了这个性能提升是吧,降低这个呃,这些出现的频率啊,这个调节相应的这个内存大小之外呢,我们再一个考虑的点说,能不能把这个对象啊。
02:09
诶,在一些特殊情况下呢,就别把对象放在堆了。那你这样的话呢,是不是尽量呢,是不是就从另外一个角度呢,就减少了这个JC,那同时呢,就能提升这个程序的性能,对吧?哎,就是这样的一个想法啊好,那我们来看一下这个,呃,深入理解Java逊中关于这个扎瓦堆呢,有这样一段描述说呀,随着这个git叫其实编译器啊,编译器的一个发展和陶逸分析技术的一个成熟,说在站上分配。啊,站上分配这个对象和标量替换技术啊,将会导致呢一些微妙的变化啊,使得呢,我们将所有的对象呢,分配在堆上啊,就变得不那么绝对了。诶,有这样的一个描述啊,那站上分配和这个标量替换,咱们下边呢,也会做介绍啊。下边呢,说说对象呢,在堆中进行内存的分,呃,进行对象的分配啊,这的是我们的一个常识啊,这个大家呢都清楚,那现在有一种特殊情况说呀,如果要是经过这个叫逃逸分析啊,Escape analysis是吧?啊如果经过逃逸分析之后呢,发现一个对象并没有啊,逃逸出一个方法的话呢,我们就可以把它考虑啊,考虑成呢叫占上分配。
03:17
啊,这样呢,就无需在这个堆上呢去分配内存了,诶也无需进行垃圾回收了,啊这个毫无疑问我们就能够提升一个诶程序的性能是吧?诶这呢,这个我们提到这个叫做逃逸分析,这也是咱们现在比较前沿的一个Java的技术。啊扎va比较前沿的一个技术,行,那这呢出现一个词啊,叫做逃逸啊一说到这个逃逸呢,大家生活中呢,啊应该也接触过是吧?啊一说呢,这个畏罪潜逃啊,我们称为呢叫逃逸,就是你在规定的这个圈之外呢,你跑出去了啊,这叫逃逸的行为是吧?啊这是一个哈,那还有一种什么样的行为啊,就是咱们以前学这个物理的时候呢,说这是地球啊,咱们扔个东西呢,肯定就落到这个地面上了,说当你这个速度呢,要是达到这个七点九千米每秒的时候。
04:03
啊,就是我们所谓的是不是第一宇宙速度啊,哎,达到这个七点九千米每秒的时候呢,你能够绕着地球呢去旋转啊这呢,我们就变成一个卫星了。啊,像咱们这个人造卫星呢,都是这样的一个原理啊,那如果你要是超过七点九千米每秒的时候呢,是不是我们这个轨迹呢,可能就是个椭圆,你要达到这个,呃,第二宇宙速度,刚才说的7.9其实是第一宇宙速度是吧,你要达到第二宇宙速度的时候呢,是不是我们就变成太阳的一个行星了。对吧,哎,这呢是我们讲的这个物理上的这个逃逸。啊,那相似之处呢,都是它脱离了我们原有的这样的一个范围啊,我们就称为是一个逃逸。那我们这里边儿所说的这个逃逸是什么意思啊,就是说哎,你这个对象看看呢,有没有逃逸出这样的一个方法,如果你逃逸出这个方法了。啊,如果你逃逸处理方法了,我们就不进行账人分配,你要没有逃逸处方法呢,我们就进行占上分配。啊,就是这样一个道理啊,咱们一会儿呢,细节展开啊,这呢是我们可以考虑的一个技术,那还有什么技术呢。
05:03
哎,这呢就提到了基于open jdk深度定制的,哎淘宝的虚拟机啊,淘宝虚拟机像现在这个,哎淘宝呢,在咱们国内呢,应该算是首屈一指的,这个也算是技术型的这个企业了啊,也有很多这个开源的一些这个工具都是淘宝呢阿里巴巴呢开发出来的啊,这个使用的量呢也很大啊,那淘宝呢,像呃天猫啊,淘宝本身啊,这个使用的这个虚拟机啊,都是这个他自己定制的啊,毫无疑问啊,那这个在淘宝虚拟机里边,他提到了一个创新性的技术叫GCH。啊,JC啊,Invisible heap啊,它能够实现呢,叫offset,就是将生命周期较长的这个Java对象从咱们这个堆中呢,给它移出来,那移出来如果你要放在站上呢,就是我们上面提到叫占上分配,如果你要是把它放在另外一个区域里边啊,实际上呢,用的还是我们这个本地的这个内存是吧?啊,你要放在这个啊offset对外的话呢,诶,我们就认为你这是用的这个淘宝虚拟机里边它这样一个技术,它这个技术的特点什么呢?就是JC呢,根本就不考虑啊JCH内部的这个对象。
06:09
啊,就相当于我们把这个对象啊放到这个对外以后呢,不再需要进行JC了啊,自然而然你就降低了JC回收的频率是吧,哎这呢就实了,实现了一个形成的提升啊就最终目的呢,咱都是希望呢,呃,就是在这个堆上分配对象的机会呢少一些。啊,另外呢,这个咱们也讲到了这个堆中呢,是共享的,所以呢,在我们分配对象的时候,超出了这个t lab。哎,超出了我们这个TLB能够承载的这个呃范围的时候呢,它是不是还要考虑到一个加锁的问题是吧?啊这呢都会导致我们这个呃性能呢会降低。好,那这块呢,我们重点呢,就不再说这个淘宝的虚拟机了,咱们来看一下,上面提到这个叫逃逸分析。好,那如何将哎对象的对象呢分配到占呢?哎,我们使用叫逃逸分析啊,下边还提到逃逸分析的这个好处,这呢一会儿咱们都会讲到啊,这是同步负载呢,也涉及到我们叫同步同步省略啊,诶这个不再堆上分配量呢,就降低这个堆的一个分配压力是吧?好,下面我们看一下啊说呢,通过陶逸分析houseport编译器呢,能够分析出来一个对象,它的这个引用的作用范围,诶从而呢决定是不是需要把它分配到对上啊下边一个说明说当一个对象。
07:24
在方法当中被定义以后。哎,在方法当中被定义以后呢,如果你这个对象只在方法内部使用,哎,我们就认为它没有发生逃逸。那如果说你在方法外部被引用了呢,我们就认为你呢发生了逃逸。啊,那如果你要是没有发生逃逸,咱们呢,就使用叫战上分配。注意,是没有发生逃逸的。嗯,来,那下边我们给大家举例子啊,比如说呢,像我们这个方法当中呢,我定义了一个呃变量,然后呢,诶new了个对象,主要呢,我们看new的这个对象啊,我现在圈入在这部分呢,它是需要是不是放在这个堆空间中的,对吧?好那么这个对象呢,在内部使用了之后呢,最后我把这个变量呢,诶复制no。
08:09
哎,就是堆空间这个对象呢,就没有任何的引用的指向它了,哎,那我们说呢,这个对象它的作用空间就只在我们这个方法内部。哎,咱就认为呢,他没有发生逃逸。那那这时候呢,我们这个U的这个V的对象就可以把它放在这个占空间中。哎,就可以把它放在占空间中啊,那我们想下是为什么放在占空间中就没事呢?哎,大家想咱们这个占空间啊,它首先呢,是不是每个线程一份。对吧,首先呢,它就不会涉及到这个同步的问题啊,咱们可以去并行的去执行各个线程啊,这是一个,那其次的话呢,咱们这个每一个站里边放的呢,都是一个一个的战争。诶,放在一个一个战帧啊,那这一个战帧呢,对应的就是一个方法的调用里边呢,有局部变量表,有操作数站啊,有这个动态连接啊等等,还有一些附加信息,哎这样的情况,那么当我们这个方法执行完以后呢,是不是直接我们这个战争呢,就弹出战了,那你下边这个方法当前的战争是吧,执行完以后呢,它又弹出战了。
09:10
那一旦弹出站以后啊,那其实呢,我们这里边这个空间呢,也算是就得到了释放,根本呢,在我们这个占空间当中,是不是就不存在着这个GC,那这样的话呢,就不会影响到我们这个程序这个性能算是一种自动的这个内存释放是吧?那所以呢,我们要是能够在站上分配对象,那当然是很好的事情啊,那这呢,就是我们用的这叫逃逸分析。啊,那我们再举个例子,这呢我们是一个方法啊,两个string型的变量在这个方法内部啊,我们用了一个string buffer。啊,这个变量名呢,叫。啊,听着有点怪怪的是吧?嗯,啊行啊这个变量,那么这个对象呢,我们向它内部呢,添加了两个字符串,最后呢,我把这个呃,String buffer的对象呢给他返回了。啊,这人一返回是不是就出事了,大家想我们呢,在另外的一个方法当中,咱们呢,是不是可能会去调这个方法,我一调的话呢,诶,你就拿到了这个返回的这个变量,实际上呢,就是相当于我们这个返回的接收的这个变量呢,是不是就指向了我们new的这个buff了。
10:13
那相当于你这个string buffer有可能是不是在这个方法外部被调用啊。那那这时候呢,你是不是就认为叫发生了,是不是逃逸啊,因为你在外边可能会被调嘛,所以这个对象呢,咱们就不能够使用战场分配了。啊,就不能使用站上分配了,这要注意啊,那我们就必须呢,得放在堆里,老老实实放在堆里是吧?哎,什么情况能放在这个站里边,就是你就是让你当前那个方法用内部用,用完以后呢,就拉倒,要不别人也不用,诶我们就用这个叫占上分配是吧。那上面这个代码咱们怎么能够稍微的改一改,就让它可以诶实现呢,逃逸分析或者呃,这个就是让他这个没有发生逃逸分析,能够在站上进行分配呢,诶我最后呢,就做了一个这样的简单操作。
11:01
我们呢,让这个string buffer的对象呢,调一个to方法,哎,这呢,实际上呢,就是我们又拗了一个string,然后让这个内容呢,跟我们这个string buffer呢,这个内容呢是一样子的,把这个stream stream对象呢返回了。那我们这个Li buffer呢,是不是就在它内部被使用,外部呢也不用诶那此时呢,它诶就可以战上分配了。啊,非常巧妙的一个操作是吧,好,那么通过刚才这样点两个典型的操作呢,大家应该对逃逸分析呢,哎,有一定的了解了,那么实际当中呢,我们到底有几种情况呢?诶这块呢,咱们来看这个代码。啊,这个代码呢,同样呢,提前呢,我们给大家已经写好了,把这几种情况呢,咱们都说一说,哎,大家能够去理解就可以。好,来我们看一下啊,那这呢,我们是声明了当前类中的一个属性,哎,生明一个属性,那我们去调这个get in instant这个方法的时候,你看啊说如果你这个OB be呢,是一个no,哎,No就是空啊,如果要是true呢,我们就给你拗个对象,如果你要不是处呢,我们就直接返回这个OB啊,总之呢,我们返回这个一定是非空的是吧?那在这里边大家注意,就有可能你这已经是非空的了啊,那你非空的话呢,外边用那肯定是发生逃逸了,那要是你在诶里边呢,去new的对象那里边扭的对象也可以传到外边了,是不是照样呢,也发生逃逸了。
12:20
哎,所以呢,哎,大家去判断说有没有发生逃逸呢,就关注一下我们new的这个对象是不是有可能在外部被使用啊,这呢我在这写一下啊。啊,如何快速的哎判断哎是否哎发生了。诶,逃逸分析。哎,大家呢,就看这个new的对象。呃,是否有可能?哎,在这个方法外呢,被调用。诶就可以了,这呢就是我们参考的这个标准。
13:02
哎,就看我们用的这个对象是不是有可能在方法外呢被调用好,那第一个呢,显然呢,我们外部呢,是不是有可能会调对吧?哎,这是这个问题,然后下个呢,这更是了,我们给当前这个属性赋值啊,那你负的这个值呢,这个属性呢,当然有可能在其他方法中被调用了。哎,所以呢,它也发生调音,这是毫无疑问的啊,哎,那么这块呢,大家思考一下。在思考问题哈,说如果,哎,当前的。嗯,当前的我们这个obj,哎,这个引用是吧,哎,声明为。诶,他如果呢,要是诶生命为这个static的,哎,他呢,还会发生逃逸吗。哎,这个我们说是不是仍然,哎会发生是吧,逃逸。哎,那大家想啊,就是我们这个是不是静态,哎跟咱们这块呢,是不是在方法外呢,被用到,是不是都一样的道理啊,哎只要呢,你传出去了,哎那就有它就有可能会被外部调用啊,所以有仍然发生了逃逸啊行,然后再看这儿啊,这个呢是没有发生逃逸的例子,就是我们说的比较经典的,呃,你只有在这个方法内部呢,New的对象,然后在内部使用了,也没有往外返回,哎它呢就是没有发生逃逸的,哎它呢是可以实现叫战上分配的。
14:25
哎,这个注意是吧,哎,然后下边这个啊说呢,我们这里边调用咱们刚才上边的叫get instance一个方法。那调用完这个方法以后呢,肯定能够返回一个对象了,诶一定是非空的是吧?啊那么把这个对象呢,放在这儿,诶说这个时候呢,会发生逃逸吗。啊,有的同学呢,在判断的时候呢,这个经常会判断错啊,说呢没有,你看这个E呢,就在里边使用,也没返回啥的是吧?诶这个大家一定要小心啊,咱们现在呢,要判断的是你new的这个对象实体,我再给你加一个吧,这个对象实体是不是能能被方法外调用,说白了就是我们这个实体它才是咱们放在堆空间中的这个结构,你这个变量的话呢,是不是有可能是在站里边的是吧?所以咱们关注的不是这个变量啊,这个大家一定要小心,那像方法里边的这个变量,这个E它是不是就在这个。
15:16
九边量表里边分配的是吧?哎,咱们关心的不是它啊,这一定要注意,咱们关心的是你拗的这个实体,那我们这个内部拗的这个实体呢,是不是就是你,哎可能是上面扭的,他也可能是本身就有了这个对象了。对吧,哎,就是你是从外边传进来的,里边用,那你这个呢,本身就不归你这个方法所有的,当然了,人家这块也是会发生逃逸的啊,你直接调这个方法,人家给你传过来,这个对象本身就是外边的啊,那一定也发生了逃逸。啊,这要注意啊,包括呢,就是如果我们上来的话呢,直接就通过这个叫get instance这个方法,比如咱们就去调啊他的叉叉叉这个方法一样的道理,哎,这个呢,我们说同样啊会发生逃逸,诶这个要注意。
16:03
啊,这要注意行,那么关于我们说发生逃逸呢,其实就是这种这几种情况啊,这个没有发生啊,那么参照标准呢,就是它只要呢没有发生逃逸,我们呢,就可以考虑使用叫占上分配。啊,就使用占上分配行,那接着我们往下看啊说呢,在咱们这个JDK6UPDATE23这个版本之后,或者呢,大家就直接啊记我们这个JDK7也行是吧?啊有的时候我们不会关注这个小版本了,直接就说七从七开始呢,咱们这个逃逸分析呢,就默认呢就开启了。那如果呢,你要是使用的这个版本比较早的话呢,记着使用我们这个参数啊,Do啊,Escape analysis哎,使用它那显示的来打开这个叫套语分析啊嗯,我这儿就不带着大家来命令行去演示了吧。哎,我就不演示了啊,这个呢,大家可以通过这个命令行去演示一下,你看看诶是不是开启了这个叫逃逸分析啊,这个指令呢,咱们前面也都讲过类似的了。行,这这个啊,然后的话呢,大家还可以通过这个,呃,这个print啊,Escape analysis啊去查看呢,逃逸分析的一个筛选结果啊,这个咱们做一个了解就行啊,做个了解就可以了,行,那么这个逃逸分析,如果我们判断完以后的话呢,这个如果没有发生逃逸,咱们可以实现呢,叫战上分配,这就咱们一会儿呢进行优化的这样的几个策略之一啊,叫占上分配,行,那么基于咱们刚才讲的这个逃逸分析啊,咱们得到一个结论就是在开发当中,咱们能使用局部变量的,是不是尽量呢,就不要在方法内,方法外呢再定义啊。
17:30
哎,这句话呢,大家是不是能有一个深刻的一个理解呢。基于两方面对吧,一方面呢,就是咱们没有讲这个逃逸分析的话呢,以前咱们要定义一个局部变量,那你确实能在方法内定义的就在方法内了,这样的话呢,这个呃,即使方法内定义这个变量,我们放在这个对空间中呢,哎,对象实体用在这个对空间中,这样呢,你这个呃方法呢,在出这个站的时候呢,本身这个对象呢,也会在在适当的时候呢,是不是被垃圾回收就回收掉了。对吧,哎,就是这个呢,也不用始终给他保留着,你非得给它定义,定义成个属性,不是这个方法内的一个局部变量,定义成属性,那可能后边也不用,那你这块呢,就哎JC,每次JC还不释放这个空间啊,占用着呢还不太好。
18:12
是吧,就是按照以前咱们讲的这个思路也是啊,能用局部变量呢,咱们就不会用这个属性去声明啊,那更不会考虑静态的问题,对吧?啊,那么另外一个点呢,就是说基于我们现在讲的,你要是使用这个呃局部变量来定义的话呢,我们呃不管像以前一样哈,你可以提早回收,现在呢,还可以考虑把这个对象呢,直接就放在我们这个占空间。啊就更高级了,就是这样的原因,行,那这呢,咱们就把这个逃逸分析呢,哎给大家呢,解释清楚了。
我来说两句