Java面试题之JDK

愿天堂没有BUG

共 2409字,需浏览 5分钟

 · 2021-05-10

一,JVM内存模型

根据JVM的规范,JVM内存共分为虚拟机栈,本地方法栈,堆,方法区,程序计数器五个部分。


线程私有:

程序计数器:是当前线程所执行的字节码的行号指示器,每条线程都有一个独立的程序计数器,这类内存也称为“线程私有”的内存。正在执行Java方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址)。如果是Natice方法(指一个Java调用非Java代码的接口0,则是空。

虚拟机栈:每个方法在执行的时候也会创建一个栈帧,存储了局部变量,操作数,动态链接,方法返回地址。每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。通常所说的栈,一般是指在虚拟机栈中的局部变量部分。局部变量所需内存在编译期间完成分配,如果线程请求的栈深度大于虚拟机所允许的深度,则StackOverflowError。如果虚拟机栈可以动态扩展,扩展到无法申请足够的内存,则OutOfMemoryError。

本地方法栈:和虚拟机栈类似,主要为虚拟机使用到的Native方法(指一个Java调用非Java代码的接口)服务。也会抛出StackOverflowError

线程共享:

堆:被所有线程共享的一块内存区域,在虚拟机启动的时候创建,用于存放对象实例。对可以按照可扩展实现(通过-Xmx和-Xms来控制)当队中没有内存可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。

方法区:被所有方法栈共享的一块内存区域。用于存储已经被虚拟机加载的类信息,常量,静态变量等。这个区域的内存回收目标主要针对常量池的回收和堆类型的卸载。

二,方法区JDK1.6至JDK1.7值JDK1.8的变化。

第一段代码,判断JDK1.6与JDK1.7和JDK1.8的区别:



解答:假如在JDK1.8或者JDK1.7的环境下运行,答案是true。但是在JDK1.6环境下,返回结果是false。

原因:字符串常量一般放在常量池(Constant Pool)中,但是在JDK1.6环境下,常量池放在永久代(PermGen)中,在执行str2.intern()之前,String str2 = new String(“test”) + new String(“01”);通过生成了多个对象,str2最终指向Java堆内存中的“test01”的引用地址。在执行str2.intern()时,因为常量池中没有“test01”这个字符串,会在常量池中生成该字符串的拷贝,将此字符串常量添加到常量池中。在进行String str1 = “test01”字面量赋值的时候,常量池中已经存在该字符串常量,就直接返回了该字符串常量在永久代中的引用地址,因此当调用str2==str1的时候,**用Java堆内存中的引用地址和永久代中的引用地址进行比较,一定返回false。**那JDK1.7和JDK1.8的返回结果为true,是不是说他们俩str2和str1指向的是同一个内存的引用地址呢?答案确实是这样。
从JDK1.6到JDK1.7,HotSpot虚拟机,关于永久代中的内存分配模型发生了变化,其中一部分就体现在永久代中常量池的变化,
JDK1.7之后将字符串常量池从永久代(PermGen)中移动到Java堆内存中了

第二段代码JDK1.7和JDK1.8之间的区别:


JDK1.7的运行结果:


JDK1.8的运行结果:


在JDK1.6环境下,抛出OutOfMemoryError:PermGen space,永久代空间不足。

在JDK1.7和JDK1.8环境下,抛出OutOfMemoryError:Java heap space,堆空间不足。

通过上面的报错信息也正好印证了咱们上面说的将常量池由永久代移动到了Java堆内存中。但是通过比对JDK1.7和JDk1.8的报错信息咱们也可以看到,相比于JDK1.7,上图中JDK1.8的报错信息中多出了一部分红色的警告信息。Ignoring option PermSize/MaxPermSize= XXM;support was removerd in 8.0;意思就是,忽略这两个参数,这两个参数已经被删除了。

这是因为从JDK1.8之后,永久代(PermGen)被完全地移除了,所以永久代的参数-XX:PermSize和-XX:MaxPermSize也被移除了。
对于JDK1.8, HotSpots取消了永久代,那么是不是也就没有方法区了呢?当然不是,方法区是一个规范,规范没变,它就一直在,只不过取代永久代的是元空间(Metaspace)而已。

在原来的永久代划分中,永久代用来存放类的元数据信息、静态常量以及常量池等。现在类的元信息存储在元空间中,静态变量和常量池等并入堆中,相当于原来的永久代中的数据,被元空间和堆内存给瓜分了。

相比于之前的永久代划分,Oracle为什么要做这样的改进呢?

在原来的永久代划分中,每当一个类初次被加载的时候,它的元数据都会放到永久代中。但是永久代的内存空间也是有大小限制的,如果加载的类太多,很有可能导致永久代内存溢出;同时,永久代大小也不容易确定,因为这其中有很多影响因素,比如类的总数,常量池的大小和方法数量等,但是PermSize指定太小又很容易造成永久代内存溢出;同时,HotSpot虚拟机的每种类型的垃圾回收器都需要特殊处理永久代中的元数据。永久代会为GC带来不必要的复杂度,并且回收效率偏低。将元数据从永久代剥离出来,不仅实现了对元空间的无缝管理,还可以简化Full GC以及对以后的并发隔离类元数据等方面进
行优化。


浏览 16
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报