00:00
那接下来呢,咱们来看一下这个方法区的具体的一个理解,这呢也构成了咱们方法区的最基本的几个规则,那这里边这个细节呢,需要大家能清楚,好,那我们来看这个PPT,那一谈到这个方法区的一个理解啊,那咱们最先想到的一个标准呢,那就是官方文档,那官方文档呢,说的是最详细也是最准确的,对吧?诶,我们所有的内容呢,其实都来自于这个官方文档这样的一个最根本的一个标准,呃,就好比是咱们国家的法律是吧,都得是基于这个宪法,然后呢,往外去延伸出来的啊行,那么关于我们这个方法区在官方文档上的说明呢,对应的这个链接地址我已经放到这儿了,诶大家呢,直接把这个链接地址呢,放到咱们这个网这个浏览器地址栏啊,输入进去就可以是吧,诶这个呢,其实大家也直接去我们这个Oracle官网上去找就行,我这儿呢,这个咱们在前面一开始讲这个扎va虚拟的时候呢,其实说过啊,诶直接呢去这个路径去找,这儿呢,我已经把这个障碍虚拟的具体的规范呢,哎,这个列出来了。咱这呢用的是这个八的版本啊,前面咱们也说过了,八呢是咱们现在在开发当中用的版本最高的频率最高的一个版本,所以这呢,咱们也以这个八为版本呢,主要来进行说明,那么后续的版本会不会有很多变化呀,咱知道到目前为止呢,已经它是已经是不是到这个JAVA14了,三月份发布的是吧,JAVA14那中间呢,已经经过了好几个版本了啊,那会不会有很多变化呢?这里边给大家强调一下,针对于咱们现在讲的这个方法去。
01:24
呃呃,不是方法区,或者我们叫这个内存区是吧,诶打开我这个啊。针对于咱们现在讲的这个内存的这块,其实变化不大,这块呢变化不大啊,就是咱们说这个678会有一些变化,像咱们这个方法群呢,都会有一些变化,那么八以后跟咱们这个八啊,基本上在这个运行时,数据区这块呢,没有什么变化,那主要变化在哪呢?在我们这个叫garbage collection,在咱们这个JC这块后续的版本中么,变化是比较大的。啊,这个呢,咱们讲到这个垃圾回收的时候呢,带着大家去说啊,那咱们现在讲到这个内存的这个结构呢,诶八之后的版本跟八基本上是一样的,所以这呢就给大家不用担心了,那咱们呢,直接找到这个叫运行式数据区,其中呢,2.5.4呢,就是专门来说我们这个叫方法区的,那咱们把它打开,诶看一下这里边呢,都描述了哪些信息,OK,那这块呢,我把它这个稍微再放大一点。
02:14
来咱们呢,一起来翻译一下这里边这个内容,看大家这个英语水平呢,是不是OK啊,那当然了,这块翻译完以后,咱们在下边PPT中还有一些描述啊,跟它是类似的,这个基本的这个理解也是来自于我们这块的这个核心内容,咱们给他展开的行看一下啊。说呢,Java虚拟机它有一个方法区,这个方法区这个定语从句说呢,它是一个share的啊,就共享的在这个所有的Java虚拟机的这个线程当中。那这句话呢,想描述的点呢,就是我们这个Java虚拟机中的这个方法区是被多个线程所共享的,它属于一个共享区域,没问题,好下一个就跟咱们堆一样啊,说这个方法区啊,它是一个。这个呢叫类似的,诶跟什么类似啊,说存储在,嗯,存储的一个区域编译的代码啊,是一个叫传统的语言啊,这个怎么整合呢,就是它呢,跟我们说传统语言编译好以后的这个代码,存储的这个区域呢,是类似的。
03:15
传统这个语言这个编译好以后呢,我们有一个存储区域是吧,哎,这个呢,我们说方法区呢,存放的其实也是咱们主要的自建码指令,诶这个类型信息是吧,都放在这个方法去,那还跟谁类似啊,跟我们说操作系统进程当中的这个叫诶文本片段是吧,它是类似的。啊,那没有接触过这个呢,你也就不用多多关心了是吧?好下一个说呢,它存储的是一些类的结构啊,类的一些结构,比如说呢,运行时常量池啊,比如说呢,这个属性和方法的一些数据,包括呢,这个方法和构造器的一些,这个叫自解码是吧,这个code端翻译成自解码了,OK,然后呢,还包括呢这个,嗯,这个我们说具体的一些方法,或者特殊的一些方法,呃,用于我们说类或者是实例的一个初始化,或者说接口的一个初始化,诶这呢就强调说我们类实例和接口初始化的时候呢,一些特殊的方法啊,比如说呢,我们这个实例的时候呢,咱需要用到呢,叫in这样的一个方法。
04:14
OK,那这个方法的话呢,我们需要调用这个对应类的这个构造器,那对应的这个呃子解码指令的话呢,我们叫evoke special是吧,那这样的一个接口啊,行这个就过了,然后说这个方法区啊,它被创建在Java虚拟机启动的时候,也就是说虚拟机启动我们这个方法区呢,就哎创建好了,跟咱们说这个堆呀站呀等等都是一样的的。说虽然这个方法区它是逻辑上的堆空间中逻辑的一部分,哎这呢,就是我们一会要强调的,就是逻辑上来讲呢,说我们把这个堆空间,把这个方法去呢,看成是堆的逻辑上的一部分,但是呢,具体的一些,简单的一些,这个Java虚拟的一些实现。啊,具体的一些呃,扎虚拟标准的一些具体的虚拟机的实现呢,他可以选择呢,既不进行垃圾回收,也不进行一些压缩。
05:03
那也就是说呢,呃,逻辑上呢,我们把这个方法去看成是堆的一部分,但是我们说堆的话呢,都有JC垃圾回收,然后里边也涉及到一些压缩算法啊,就是避免我们这个碎片的问题,那咱们这个方法区的话呢,是可以不去实现JC的,也可以呢,不去进行压缩这个来处理这个碎片的问题,这常是我们后边讲JC的时候,这个压缩算法,那言外之意呢,就是我们可以考虑把方法去呢,就独立于咱们这个堆空间呢去存在,哎,就这个意思啊,一会儿咱们看中文的这个说明说呢这个呃,Specification就是咱们当前这个GM java8的这个版本。说呢他没有,呃,这叫限定咱们这个方法区,它具体的这个位置啊,以及的话呢,他也没有,呃,这叫管理,这个编译好以后这个代码它的一个叫策略。啊,什么意思啊,就是说咱们没有说虚拟的这版本,没有要求说你这个扎法区,这个这个方法区说一定要是对的一部分,或者说不是对的一部分啊等等是吧,也没有要求说到底是方法区里边应该都存具体哪些东西,那比如说字符号常量池啊,你这个,呃,还有我们说的这个静态变量啊等等是吧,咱们说的这个JDK呃,678等等,这个过程当中它会有些变化,这个Java虚拟呢,通通呢,都没有特别明确的去说明。
06:19
啊,这个Java虚拟呢,有点像咱们Java里边讲的接口一样是吧,具体的虚Java虚拟规范相当于这个接口,然后具体咱们讲的虚拟机呢,相当于是它的这个实现,那在这个接口当中呢,其实就描述的就稍微来讲呢,是比较这个宽松一些啊就这意思说这个方法区啊,它可以是一个确定的大小也可以呢,哎,通过我们这个运行的时候呢,去动态的一个扩展。啊,说的很清楚,跟咱们这个对空间是一样子的是吧,咱们可以有相应的这个参数呢,去设置它的一个确定的大小,也可以呢,让他动态的去expanded啊,就是动态的去扩展,然后呢,它可以被呃,这叫什么呀。自动的收缩,如果说呢,你这个啊,变的就是这个方法去更大的一个方法去变的不是那么必须的时候还necessary啊,那它还可以呢,自动的去收缩压IG呢,就是说你要是固定大小了,这不用多想了,你要是让它可以动态的去这个扩充,那它自然而然的,当你用不了那么大空间,它也会动态的进行一个压缩啊这样意思说这个memory,说这个区域的话呢,啊,方法区的这个内存它嗯不要求是一个连续的啊,真的跟咱们堆是一样子的,我们说哎这个物理上呢是可以。
07:32
诶是不是不连续的,哎,我们说逻辑上的要求它是个连续的啊,就这样个意思,行这呢是我们把这个内容呢,简单的给大家一个翻译了一下啊,诶这个顺便呢,大家也看一看这个英语里边的一些关键词啊,那么我们把这个内容啊在精简一下这块呢,我们来看一下我这描述的这样的几个基本的情况,好说Java虚拟机规范中明确说明,尽管所有的方法区在逻辑上呢,属于对的一部分,刚才我们是不是都看到这个事儿了啊说但是呢,一些简单的虚拟机的实现啊,可能不会选择去进行垃圾收集或者进行压缩,这是压缩算法,刚才呢,明确的我们翻译了这句话。
08:09
那对于hotport这个扎虚拟机来讲,咱们说方法区呢,还有一个别名叫做废堆,目的呢就是要与堆这个分开。能不能理解这个问题?这个单咱咱们在前面讲这个堆的时候呢,其实也稍微提到过啊,也就是呢,在咱们这个house包这个虚拟当中啊,这个首先说呢,虚拟机规范当中,咱们把这个方法区看成是堆的一个逻辑部分,但是呢,在具体的实现上,比如说咱们说的叫houseport虚拟机,那我们把方法区跟堆呢,就给它分开,就把它认为是两个不同的结构。啊,那这个结构呢,咱们也可以起个名,那就叫做飞堆啊,就non hip啊,废堆空间是吧,目的呢就是要与堆这个分开。有的时候我们在这儿常举一个例子,或者拿这个呢去说明一下哈,说呢,这个从这个法理上来讲,说在这个台湾属于呢,咱们说中华人民共和国的一部分。
09:03
对吧,诶,那但是呢,在实际当中呢,这个台湾呢,它还有一个别名啊,它的别名呢,呃,就叫做台湾,目的呢,就是要与这个大陆呢分开。哎,就是现在呢,这个台湾目前的一个现状是吧,诶这个大家去体会一下,就是从这个逻辑上呢,我们规程是一个整体的,但是现在事实上呢,我们在具体的落地实现上呢,这个方法去跟堆呢,诶我们可以看到是两个不同的结构啊这样的,那么呃,当然这个咱们迟早有一天还是要把台湾收了啊,当然这个house斯旺逊尼的话呢,咱们后边呢,不会说把方法区又归到这个堆里边,这这不会了啊,这不会啊行,那么这个。咱们再从其他角度上去理解一下呢,诶其实还可以这样来说,大家想咱们讲这个堆的话呢,它的主要目的呢,是不是就是来存放咱们说new的这些对象,咱们new的对象呢,都放在这个堆当中,对吧?那那么大家想我们方法区里边放在其中一类结构呢,就是咱们说的这个类的信息啊,类型信息class,那很显然我们这个类class它不像堆一样,是你在这个程序代码中我们去new的东西啊,就是通过代码中我们这个创建生成的一些结构呢,咱们放到堆里边,你这个类本身呢,不是代码生成的,所以说呢,咱们本身呢,也不是new的对象,你也不应该考虑放到堆。
10:16
是吧,所以说呢,这个堆跟方法区呢,从这个角度来讲呢,确实应该把它俩分开啊,不太一样对吧,这是其一,那另外一个角度呢,就是咱们在实际上落地实现上来看的话呢,咱们在设置堆的那个大小的时候呢,也确实没有影响到方法区的一个大小啊,举个例子啊,比如说咱们就来看一下这个代码。这个代码呢,是咱们之前讲这个堆的时候用过的非常简单的一个代码,这开始这结束,中间呢,让他呢去sleep一下1000秒行,那接下来的话呢,我们给当前的这个程序啊,我找到这个程序。Area OK啊第九章的没问题,我这呢给大家设置这个参数,设置咱们这个堆空间的参数,比如呢,我就设置成这个600兆。行,这是我们设置这个堆空间的一个初始和最大的一个这个大小,那设置完以后呢,我们把这个程序呢,给它跑起来。
11:07
行,抛起来以后呢,咱们再使用这个叫呃接VVM打开找到咱们当前的这个进程。那在咱们这个微JC当中,大家来看,那此时的话呢,我们这个叫呃,伊甸园区新增者零区,星者一区,加上我们这个O的区,这几个部分加一起啊,一算是不是就已经是600了。就这几个部分,咱们并没有去包含,这叫ma space,这个ma space呢,就是咱们说方法区的一个具体的落地实现。啊,所以说呢,我们设置的这个对空间的参数啊,本身你也没有包着人的一个方法去,所以我们也可以看成是呢,哎,这样就是一个独立的结构,诶大家可以这样去理解,OK。行,这呢是我们从这个呃,结尾素VM当中这个角度呢去看了看,那通过其他的这个可视化工具呢,也是一样能看到这个效果的啊,这呢就不多说了,所以说呢,我们把这个方法去呢,可以看作是一个独立于堆的一个内存空间啊,这呢大家应该非常明确了,好,接着我们往下看啊。
12:05
好,接着往下看,说这个方法区啊,和对一样,是各个线程共享的一个区域。诶,各个线程共享区,刚才咱们看这个英文的时候呢,也说到了,那这里边涉及到一个什么点呢?就是既然你是个共享区域,如果我们这里边儿有两个线程或者是多个线程,我们都需要呢,使用某个类的时候,而这个类呢,之前没有加载过,那就相当于我们只能是有其中的一个线程,是不是调用一下这个class o,把我们这个类进行加载啊,啊那么其他的这个线程,如果你要使用同样的这个类的话呢,他们都得等待啊,言外之意呢,就是我们只需要加载一次就可以,这呢就是体现了还是一个共享的问题啊,这个大家注意一下,然后呢,说方法区呢,在虚拟机启动的时候呢,就被创建,刚才我们看英文呢也说到了,说呢,它的实际物理内存跟Java堆呢是一样子的,可以不连续啊,这个咱们在刚才看这个文档的时候呢,最后也说了,说可以不连续是吧。诶很清楚啊,说这个方法区的大小跟堆空间一样,可以设置成一个固定大小,或者是可扩展啊,这个也也也能理解啊,这个咱们后边呢,下一个就讲一下这个方法区我们如何去设置它这个大小啊,如何是这个大小行,下一个说呢,方法区的大小决定了系统可以保存多少个类。
13:16
哎,主要呢,我们这个方法去呢,就是来存储咱们这个类信息的,说如果系统中啊定义了太多的类,就会导致这个方法区呢溢出,那如果说你是一个供应大小了乘不下了,那就溢出了,或者说呢,是可扩展的,但是呢,能扩展的空间呢已经没有了,那这个呢,照样的都是溢出,那溢出的时候大家注意,那这时候要小心,方法区呢,咱们具体的落地的话呢,在GDK极极之前叫永久带啊,叫这个永久带啊permanent space,那在我们这个GD8级之后呢,我们称为呢叫metapace,叫源空间,所以说的话呢,他报的这个错误信息啊,在JDK7及以前呢,是这个异常信息,那JDK7级之后呢,是我们这样个异常信息。虽然说都是OM,这个OM的话呢,咱们前面见过呢,叫hip space,这呢叫pro space,还有呢叫Meta space,还有把这个加成红色的注意一下。
14:09
那它是有一个这个区别的啊,这要注意行,那举一个简单的道理啊,比如说大家呢,简单的例子,大家看一下咱们这个代码呢,是不是写的已经是相当简洁了,那我问一下你知道这个类我们,呃,这个程序我们要执行起来的时候呢,它会帮我们加载多少个类呢?诶同学想诶这不就一个类吗?啊其实远不止于此,最起码的话呢,你这个system是不是要加带啊,你这个类的负类是不是要加带啊,Threatread是不是要加带啊,你这个异常类的是不是要加带啊,异常类的负类是不是要加带啊啊等等等等是吧?哎,包括呢,你这个print line的方法所属的是大阴流,你是不是要加载?哎,所以这里边远不止我们看到的这样,诶当前这个类呢,要被加载,那到底都有多少个呢?感受一下,我把刚才这个程序呢抛起来了,咱们再用这个JVVM打开。哎,双击。诶,然后呢,在这个监视这块,好,大家看这。
15:02
看到了是吧,当前呢,咱们这个程序呢,代码已经写的是非常简单了,在这种情况下呢,我们还加载了1666个类啊,这块呢,还在动态的变化。可见呢,是不是还是比较夸张的是吧?诶我们加载类呢还是比较多的啊行,注意一下这个问题,那如果说我们加载这个类过多的话呢,呃,因为你这个类加载的话呢,都要放在我们这个方法区了,那你很显然呢,就有可能会超出咱们这个方法区的这个大小,那就会报这样的异常。啊,那什么情况下会有这样的这个这个问题出现呢?比如说咱们一个启动类里边,如果呢,大家加载了这个,我可以在这儿稍微再说一下啊。比如说呢,咱们加载了大量的这个,呃,第三方的一些炸包加载。大量的第三方的,哎这个炸包是吧,你加载的这个炸包过多的时候呢,那这个时候里边它相应的这个类呢就很多,那就有可能会出现我们说的这个叫哎OM是吧,诶真的是方法区的一个OM,那以及的话呢,比如说咱们这个tomcat,咱呢,诶部署的应用程序太多。
16:07
啊,或者叫部部同部署的,咱们这个叫工程是吧,哎,过多。嗯,那比如说呢,咱们部署了这个30~50个啊,这已经是非常夸张了啊,部署这么多个工程的话呢,这个有可能倒成导致呢,咱们这个呃方法区啊,就出现了一个溢出的问题啊,那以及的话呢,咱还有可能会大量的生成这个呃,动态的生成这个反射类啊。哎,反射类啊,你要是这个加载的这个类过多的话呢,嗯,动态的生成啊,像我们说这个动态代理,我我们也会去动态的去创建一些这个,呃,代理类的对象是吧?呃,你要是动态的生成过多的这个反射类的话呢,我们也可能导致方法区的一个OM,这呢就是我们举的一些场景啊,大家需要注意。那尽量的我们还是呃,限制一下这个加载的这个炸包的一个个数是吧,OK哎,下一个呢,是说到这个关闭GM的时候呢,我们就会释放这个区域的一个内存啊,非常容易理解,我们呢,在启动PM的时候呢,我们方法区呢,就跟着被创建了,当你关闭GPM的时候呢,整个这个进程就结束了,我们这个方法区呢,也有相应的这个结束了啊,这就我们说的这个情况行,那么关于这里边这个基本情况呢,大家还要牢记于心啊,得弄清楚这里边提到的基本问题。
我来说两句