面试必问的 JVM 运行时数据区,你懂了吗?
2021-07-14 07:21:57Java 虚拟机的运行时数据区经常在面试中被拿来提问,很多概念在市面上有各种各样的说法,搞的不少同学应该是懵逼的。
前言
Java 虚拟机的运行时数据区经常在面试中被拿来提问,很多概念在市面上有各种各样的说法,搞的不少同学应该是懵逼的。
当我们陷入不知道哪个说法是正确的情况时,最好的参考就是源码和规范。
在面试中,当面试官反问你:为什么某某是这样?的时候,如果你回答:因为规范是这么写的、因为源码是这么写的。
这个回答是非常有说服力的。
因此,本文在描述一些有争议的问题上,优先以《Java 虚拟机规范》的说法为准。
正文
1、运行时数据区(Run-Time>
Java 虚拟机定义了若干种在程序执行期间会使用到的运行时数据区域。
其中一些数据区域在 Java 虚拟机启动时被创建,随着虚拟机退出而销毁。也就是线程间共享的区域:堆、方法区、运行时常量池。
另外一些数据区域是按线程划分的,这些数据区域在线程创建时创建,在线程退出时销毁。也就是线程间隔离的区域:程序计数器、Java虚拟机栈、本地方法栈。
1)程序计数器(Program Counter Register)
Java 虚拟机可以支持多个线程同时执行,每个线程都有自己的程序计数器。在任何时刻,每个线程都只会执行一个方法的代码,这个方法称为该线程的当前方法(current method)。
如果线程正在执行的是 Java 方法(不是 native 的),则程序计数器记录的是正在执行的 Java 虚拟机字节码指令的地址。如果正在执行的是本地(native)方法,那么计数器的值是空的(undefined)。
2)Java虚拟机栈(Java Virtual Machine Stacks)
每个 Java 虚拟机线程都有自己私有的 Java 虚拟机栈,它与线程同时创建,用于存储栈帧。
Java 虚拟机栈描述的是 Java 方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
3)本地方法栈(Native Method Stacks)
本地方法栈与 Java 虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是 Java 虚拟机栈为虚拟机执行 Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的本地(Native)方法服务。
4)堆(Heap)
堆是被各个线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域。
堆在虚拟机启动时创建,堆存储的对象不会被显示释放,而是由垃圾收集器进行统一管理和回收。
5)方法区(Method Area)
方法区是被各个线程共享的运行时内存区域。方法区类似于传统语言的编译代码的存储区。它存储了每一个类的结构信息,例如:运行时常量池、字段和方法数据,构造函数和普通方法的字节码内容,还包括一些用于类、实例、接口初始化用到的特殊方法。
6)运行时常量池(Run-Time Constant Pool)
运行时常量池是 class 文件中每一个类或接口的常量池表(constant_pool table)的运行时表示形式。
它包含了若干种常量,从编译时已知的数值字面量到必须在运行时解析后才能获得的方法和字段引用。运行时常量池的功能类似于传统编程语言的符号表(symbol table),不过它包含的数据范围比通常意义上的符号表要更为广泛。
2、Java 中有哪几种常量池?
现在我们经常提到的常量池主要有三种:class 文件常量池、运行时常量池、字符串常量池。
3、class 文件常量池
class 文件常量池(class constant pool)属于 class 文件的其中一项,class 类文件包含:魔数、类的版本、常量池、访问标志、字段表集合、方发表等信息。
常量池用于存放编译期间生成的各种字面量(Literal)和符号引用(Symbolic References)。
字面量比较接近于Java语言层面的常量概念,如文本字符串、声明为 final 的常量值等。
符号引用则属于编译原理方面的概念。符号引用是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可(它与直接引用区分,直接引用一般是指向方法区的本地指针,相对偏移量或是一个能间接定位到目标的句柄)。符号引用主要包括下面几类常量:
常量池中每一项常量都是一个表,截至JDK 13,常量表中分别有17种不同类型的常量。17种常量类型所代表的具体含义如图所示。
关于 class 文件常量池的更多内容可以阅读周志明的《深入理解Java虚拟机》6.3.2 章节。
4、运行时常量池
class 文件常量池是在类被编译成 class 文件时生成的。而当类被加载到内存中后,JVM 就会将 class 文件常量池中的内容存放到运行时常量池中。
Java 虚拟机规范中对运行时常量池的定义如下:
A run-time constant pool is a per-class or per-interface run-time representation of The constant_pool table in a class file.
运行时常量池是 class 文件中每一个类或接口的常量池表(constant_pool table)的运行时表示形式。
因此,根据规范定义,可以说运行时常量池是 class 文件常量池的运行时表示,每个类在运行时都有自己的一个独立的运行时常量池。
5、字符串常量池
简单来说,HotSpot VM 里的字符串常量池(StringTable)是个哈希表,全局只有一份,被所有的类共享。
StringTable 具体存储的是 String 对象的引用,而不是 String 对象实例自身。String 对象实例在 JDK 6 及之前是在永久代里,从JDK 7 开始放在堆里。
根据 Java 虚拟机规范的定义,堆是存储 Java 对象的地方,其他地方是不会有 Java 对象实体的,如果有的话,根据规范定义,这些地方也要算堆的一部分。
6、字符串常量池是否属于方法区?
我认为是不属于的。
在读本文之前,我相信很多同学会有如下观点:因为运行时常量池属于方法区,所以很多同学认为字符串常量池也应该属于方法区。
但是相信看了上面的内容后,会开始意识到,运行时常量池和字符串常量池其实是不同的两个东西,当然它们在字符串解析时会有关联。
Java 虚拟机规范中对方法区的定义如下:
The Java Virtual Machine has a method area that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the “text” segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method>7、运行时常量池和字符串常量池的关联?
上面说了,运行时常量池和字符串常量池在字符串解析时会有关联,具体如下。
类的运行时常量池中有 CONSTANT_String_info(见题3表格)类型的常量,CONSTANT_String_info 类型的常量的解析(resolve)过程如下:
首先到字符串常量池(StringTable)中查找是否已经有了该字符串的引用,如果有,则直接返回字符串常量池的引用;如果没有,则在堆中创建 String 对象,并在字符串常量池驻留其引用,然后返回该引用。
也就说,运行时常量池里的 CONSTANT_String_info 类型的常量,经过解析(resolve)之后,同样存的是字符串的引用,并且和 StringTable 驻留的引用的是一致的。
8、String#intern 方法
在 JDK 7 及之后的版本中,该方法的作用如下:如果字符串常量池中已经有这个字符串,则直接返回常量池中的引用;如果没有,则将这个字符串的引用保存一份到字符串常量池,然后返回这个引用。
下面的例子可以进行简单的验证:
9、永久代(PermGen)
永久代在 Java 8 被移除。根据官方提案的描述,移除的主要动机是:要将 JRockit 和 Hotspot 进行融合,而 JRockit 并没有永久代。
而据我们所了解的,还有另外一个重要原因是永久代本身也存在较多的问题,经常出现OOM,还出过不少bug。
根据官方提案的描述,永久代主要存储了三种数据:
1)Class metadata(类元数据),也就是方法区中包含的数据,除了编译生成的字节码被放在 native memory(本地内存)。
2)interned Strings,也就是字符串常量池中驻留引用的字符串对象,字符串常量池只驻留引用,而实际对象是在永久代中。
3)class static variables,类静态变量。
移除永久代后,interned Strings 和 class static variables 被移动了堆中,Class metadata 被移动到了后来的元空间。
10、永久代和方法区的关系?
方法区是 Java 虚拟机规范中定义的一种逻辑概念,而永久代是对方法区的实现。但是永久代并不等同于方法区,方法区也不等同于永久代。
永久代中的 interned Strings 并不属于方法区,按规范:堆是存储 Java 对象的地方 ,这部分应该属于堆,因此永久代并不是只用于实现方法区。
方法区中 JIT 编译生成的代码并不是存放在永久代,而是在 native memory 中,因此可以说方法区也并不只是由永久代来实现。
11、元空间(metaspace)
元空间在 Java 8 移除永久代后被引入,用来代替永久代,本质和永久代类似,都是对方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存(native memory)。
元空间主要用于存储 Class metadata(类元数据),根据其命名其实也看得出来。
可以通过 -XX:MaxMetaspaceSize 参数来限制元空间的大小,如果没有设置该参数,则元空间默认限制为机器内存。
12、为什么引入元空间?
在 Java 8 之前,Java 虚拟机使用永久代来存放类元信息,通过-XX:PermSize、-XX:MaxPermSize 来控制这块内存的大小,随着动态类加载的情况越来越多,这块内存变得不太可控,到底设置多大合适是每个开发者要考虑的问题。
如果设置小了,容易出现内存溢出;如果设置大了,又有点浪费,尽管不会实质分配这么大的物理内存。
而元空间可以较好的解决内存设置多大的问题:当我们没有指定 -XX:MaxMetaspaceSize 时,元空间可以动态的调整使用的内存大小,以容纳不断增加的类。
13、元空间能彻底解决内存溢出(Out Of Memory)问题吗?

很遗憾,答案是不行的。
元空间无法彻底解决内存溢出的问题,只能说是有所缓解。当内存使用完毕后,元空间一样会出现内存溢出的情况,最典型的场景就是出现了内存泄漏时。
加工中心可以车涡杆吗,跟数控车床比的话,哪个效率高?
车加工中心适合加工蜗杆,由于车加工中心可以实行自动换刀,它的加工效率更高。
台式电脑蓝屏问题
电脑蓝屏的解决电视机会出现蓝屏,电脑也会出现蓝屏。 电脑出现蓝屏时还会出现一连串警告,轻者只要按ESC就能返回,重者就会出现死机,使人十分烦恼。 出现蓝屏无外有二种情况,一种是软件引起的,另一种是硬件引起的,软件引起的蓝屏故障,一般重装系统都能解决。 但也可以不重装系统来解决。 而硬件引起的蓝屏故障,要根据具体的情况分别解决。 一、软件引起的蓝屏故障1、重要文件损坏或丢失会引起蓝屏故障(包括病毒所致)。 WIN98中VxD(虚拟设备驱动程序)或(动态连接库)之类的重要文件丢失会出现蓝屏警告。 解决的办法是利用Win98的启动盘中的Ext来恢复被损坏或丢失的文件,步骤如下:(1)、先用WIN98的启动盘起动电脑,在提示符下敲入Ext命令;(2)、在提示Please enter the path to the Windows CAB files (a) : 后输入WIN98安装压缩包所在的完整路径,回车,如E:\Pwin98\Win98;(3)、在提示Please enter the name (s) of the file (s) you want to extract:后输入你丢失的文件名,如,回车;(4)、在解压路径提示Please enter path to extrct to ( Enter for current Directory): 后输入文件将被解压到的完整路径,如C:\Windows\System 并回车,后面出现的提示,只要按YES回车即可,得新启动后就能恢复系统。 注:因事先记下丢失的文件名,以便恢复)如是病毒引起,可用杀毒软件来杀毒,并及时恢复中毒前的备份;如果能启动图形界面,可采取重装主板以及显示卡的驱动程序,和进行系统文件扫描来恢复被破坏或丢失的文件,可从开始菜单内附件中的系统工具中找到;2、注册表损坏导致文件指向错误所引起的蓝屏在注册表被改动后或受到破坏,在开机或在调用程序时出现蓝屏,并且屏幕有出错信息(包括出错的文件名)。 --解决方法是恢复注册表备份,重新启动计算机,切换到MS-DOS方式,进入Windows子目录,键入Scanreg\regstore 后回车,就可恢复最近一次注册表3、在卸载程序后出现蓝屏,多数是由于程序卸载不完善造成。 解决办法:首先记下出错文件名,然后到注册表中找到以下分支HKey_LOCAL_MACHINE\Sysytem\CurrentControlSet\Services\VxD,在查找中输入出错的文件名,把查到键值删除即可。 4、在文件错误引起的蓝屏,由于软件卸载或安装时没有即时更新造成蓝屏的,解决办法:禁用注册表中该项或重装相应软件或驱动程序;由于Win98的spl和Microsoft的Vxd_补丁程序对Win98的稳定起着重要的作用,可到华军网站下载补丁Vxd_.5、系统资源耗尽引起的蓝屏。 这一类故障主要是三个堆资源(系统资源、用户资源、GDI资源)占用有情况有关。 --打开资源监视器,看一下剩余资源的情况,如这三种资源都 在50%甚至更低,就很容易出现非法操作、蓝屏、死机。 因此,必须减少资源浪费,减少不必要的程序加载,避免同时运行大程序(如图形、声音和视频软件),如加载计划任务程序、输入法和声音指示、声卡的DOS驱动程序,系统监视程序等;6、DirexctX问题引起的蓝屏故障。 如DirectX版本过高、过低;游戏与它不兼容或是不支持;辅助文件丢失;显卡对它不支持,都可能造成此故障。 升级或重装DirectX,尝试更新显卡的BIOS和驱动程序,或升级显卡。 二、硬件引起的蓝屏故障此类故障主要有以下几种:1.内存超频或不稳定。 主要出现随机性蓝屏。 恢复正常频率运行,或找一根好的内存条进行替换,一般都能解决问题,还要注意和cpu内存条很近的散热问题;2.硬件的兼容性不好引起的蓝屏。 由于DIY门使用的是兼容机,没有经过完善的监测,从而留下陷患, 如不同规格的内存条混插等,可交换内存条所插的插糟位置,或更换相同规格、厂家、批号的内存条都可解决问题。 而内存条的好坏直接影响系统的稳定性,应特别引起重视;3.硬件散热问题也会引起蓝屏。 这一类故障,往往都有一定规律,一般在电脑运行一段时间后才出现,表现中蓝屏死机或随意重启动,解决方法是很简单,不要随意超频,加强机内的降温。 4、I / O冲突也能引起蓝屏。 这种现象比较少,如果出现,可以从系统中删除带!号或?号的设备名,重新启动计算机,一般能解决。 三、预防措施总之要做到防患于末然,只要能在平时能做到以下几点,就能有效地避免出现蓝屏或死机。 1.定期对注册表进行备份,出错时能够及时恢复到原来的状态;2.尽量避免非正常关机,可减少重要文件丢失。 如 文件等;3.一般而言,计算机能正常工作,没必要去升级著如显卡、主板的BIOS和驱动程序,避免造成损害机器;4.定期运行系统文件检查器对系统文件进行检查;在系统工具中可找到5.减少无用文件的安装,尽可能不要用手工卸载或删除程序,以减少非法替换文件和文件指向错误;6.尽量避免大程序的同时运行,如果发现在听MP3时有沙沙的声音,可以肯定该故障是由内存不足造成。
Visual Basic 6.0中ByVal和ByRef的区别和应用示例!optional和缺省时的区别和应用示例!
ls好长……长话短说ByRef传递引用(即地址值,或指针,随便怎么说),ByVal传递值想象你需要阅读我的一本书,我把我的书给你就是ByRef,我再买一本送给你就是ByVal,ByRef传递后还是一本书,ByVal传递变成了两本一样的书sub doit(by*** a&) a=a+100end subsub main b=1 doit(b); msgbox(b);end sub写ByRef的话弹出101,ByVal弹出1Optional是可选的14,缺省就是默认值function add&(a&,optional b&=100) add=a+bend functionsub main msgbox(add(1,2)) 输出1+2=3 msgbox(add(1)) 输出1+100=101end sub
发表评论