JVM(一)运行时数据区

1、为什么要了解JVM内存区?
java语言,对于每个new操作,程序员无需自己动手对allocate/free内存,而是虚拟机代为处理,一旦出现内存溢出 栈溢出或者是内存泄漏方面的问题,如果不了解虚拟机是如何使用内存的,排错会很困难。

2、程序执行时,jvm把它管理的内存划分成哪些数据区域?
线程共享:堆、方法区
线程独享:程序计数器、虚拟机栈、本地方法栈

3、程序计数器的作用?
记录当前线程中字节码文件执行到的行数。
记录字节码执行到的行数两个具体作用:解释器通过改变计数器来读取指令,用于实现流程控制;在多线程的场景下,线程切换后仍然能够记录当前线程上次执行的位置。

4、虚拟机栈的作用?
虚拟机栈用于执行java方法,是Java 方法执行的内存模型,每次方法调用的数据都是通过栈传递的。每个Java方法在被调用的时候都会创建一个栈帧,并入栈。一旦完成调用,则出栈。
虚拟机栈存放的内容?
一个个的栈帧,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。
局部变量表存放的内容?
存放基本数据类型、对象引用。具体为编译期可知的八大基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。
虚拟机栈会报出的两种异常
StackOverFlow栈溢出,如果栈的内存不允许进行动态扩容,那么,当线程请求栈的深度超过栈的深度时,会抛出异常。
OutOfMemory内存溢出,如果允许对栈的内存进行动态扩容,那么,当栈内存请求扩容,却申请不到内存时,会抛异常。
在这里插入图片描述

5、本地方法栈的作用,以及与虚拟机栈的对比?
作用:用于执行本地方法native method。与虚拟机栈类似,在调用本地方法时,也会创建栈帧。

6、堆有什么作用?
唯一目的就是存放对象实例,几乎所有的对象实例和数组都在这里分配内存。
注:jdk1.7后,HotSpot 把原本放在永久代的字符串常量池、静态变量(类变量)等移动到堆中。
堆划分为哪些区域?
1.7及之前,新生代:eden,From survivor0,To survivor1;老年代;hotspot的永久代(方法区的具体实现)
1.8之后,新生代:eden,survivor0,survivor1;老年代;hotspot的元空间(也是方法区的具体实现,元空间使用直接内存,即物理内存)
注:jvm规范把方法区描述为堆的一个逻辑部分
为什么要对堆区域进行划分?
堆是jvm内存区中最大的一块区域,也是垃圾收集器管理的主要区域,进一步划分的目的是为了更好、更快地回收内存,以及快速分配内存。
对象实例如何在不同区域间转移?
和垃圾收集密切相关。
对象会首先在eden区中创建,在一次新生代垃圾收集之后,如果对象仍存活,就会移动到s0或者s1,并且年龄加一,此后每次垃圾回收之后若还存在,则年龄就会加一,当对象年龄到达一个指定阈值时,进入到老年代。
注:阈值可以通过参数-XX:MaxTenuringThreshold和 动态计算得到的age 取最小值。如何动态计算年龄???

7、方法区存放的内容?
类的加载信息(class文件加载信息)、静态变量、常量;方法区中有静态域、常量池
方法区和永久代的关系?
方法区是jvm规范中的定义,是一种规范;永久代是hotspot虚拟机对方法区的具体实现,是hotspot的概念。(jdk1.7之前)
什么是元空间?(jdk1.8以后)
元空间也是方法区的一种具体实现,用来存放类的加载信息、常量、静态变量。与永久代不同的是,它不属于jvm定义的内存区域,而是使用直接内存,即系统的内存,可以看做是对内存区域的扩展。
为什么要用元空间替换永久代?
永久代受到jvm本身内存空间-Xmx大小的限制,当加载的类较多时,容易发生内存溢出OOM。而元空间使用的是本机系统的直接内存,相当于扩展了jvm内存空间,减小了内存溢出的几率,可以加载更多的类
并且元空间使用的最大内存区域是可控的,可以使用-XX: MaxMetaSpaceSize参数设置最大元空间大小,防止系统内存溢出。

8、解释一下常量池、Class常量池、运行时常量池、字符串常量池?
前置信息:Java源文件在编译期被编译成 Class文件,Class文件中除了包含类的版本、字段、方法、接口等描述信息外,Class文件中还有一项就是常量池
首先,常量池指的就是Class文件常量池,常量池存放的是当Class文件被Java虚拟机加载进来后的 各种字面量 (Literal)和 符号引用。纠错:下图中符号引用的第一项应为 类和接口的全限定名,也即常说的全类名。
在这里插入图片描述
然后,运行时常量池也是方法区的一部分,当Class文件被Java虚拟机加载到内存后,Java虚拟机会 将Class文件常量池里的内容转移到运行时常量池里。
运行时常量池相对于Class文件常量池的一个重要特征是具备动态性。Java语言并不要求常量一定只有编译期才能产生,运行期间也可以将新的常量放入池中。
在这里插入图片描述
最后,字符串常量池比较特殊,存放在堆空间中,而不是方法区。因为String类使用频率比较高,jvm为了避免字符串的重复创建,节省空间,维护了一块特殊的内存空间。
至此,常量池就被分成了两块,一块是在方法区中的常量池,即Class文件常量池、运行时常量池,另一块是在堆中的字符串常量池。
注:字符串常量池中存放的是对字符串对象的引用,字符串对象都在堆中。
在这里插入图片描述
9、解释一下java中创建字符串对象的两种方法的区别?
注:字符串常量池中存放的是对字符串对象的引用,字符串常量的对象都在堆中。(jdk1.7之后)
两种方法分别是:采用字面量的方式创建字符串;采用new关键字新建一个字符串对象。这两种方式在性能和内存占用方面存在着差别。
采用字面量的方式创建字符串的过程:String str1="aaa";
JVM首先会去字符串常量池中查找是否存在"aaa"这个对象的引用,
如果不存在,则在堆中创建"aaa"这个对象,然后在池中保存字符串对象的引用,将池中"aaa"这个对象的引用地址返回给字符串常量str1。
如果存在,则不创建任何对象,直接将池中"aaa"这个对象的地址返回,赋给字符串常量。

采用new关键字新建一个字符串:String str = new String("abc");
首先在堆中创建一个指定的对象"abc",并让str引用指向该对象;
判断字符串常量池中是否保存了对应的字符串对象的引用,若不存在,会在堆中创建对应的字符串对象并将该字符串对象的引用保存到字符串常量池中。如果保存了的话直接返回。
注:new创建字符串时,如果常量池中无该字符串时,会创建两个对象。
即 String s1 = new String(“abc”);这句话创建了几个字符串对象?会创建 1 或 2 个字符串。
字符串常量池的缺点:每次创建字符串对象,都要进行遍历操作,用时间换空间。

10、字符串相加的时候:
如果相加的两个字符串都是静态字符串,结果会进入字符串常量池,然后把地址返回给字符串常量;
字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池。
对于 String str1 = “str” + “ing”; 编译器会给你优化成 String str1 = “string”;

如果相加的两个字符串中含有变量,则会在堆中执行字符串的相加,实质是append操作,在堆中创建一个新的字符串对象,然后拼串,再把这个字符串对象的地址返回给字符串常量。
对象引用和“+”的字符串拼接方式,实际上是先创建一个StringBuilder,再通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。因此,+ 得到的字符串常量,并不是字符串常量池中存在的对象,属于堆上的新对象。
String str3 = str1 + str2; -> String str3 = new StringBuilder().append(str1).append(str2).toString();

注:平时写代码的时候,尽量避免多个字符串对象拼接,因为这样会重新创建对象。如果需要改变字符串的话,可以使用 StringBuilder 或者 StringBuffer

11、字符串常量池比较特殊,它的主要使用方法有两种:
直接使用字面量方式创建的 String 对象会直接存储在常量池中;
如果使用new方式创建的String对象,可以调用intern()方法,可以返回字符串常量池中对应的引用地址。

12、String对象的intern方法的执行过程?str1.intern()
String.intern() 是一个 native(本地)方法,其作用是将指定的字符串对象的引用保存在字符串常量池中。
它的作用是:
如果字符串常量池中保存了对应的字符串对象的引用,就直接返回该引用。
如果字符串常量池中没有保存了对应的字符串对象的引用,那就在常量池中创建一个指向该字符串对象的引用并返回。

注:在输出中调用intern方法并没有什么效果,仍然会输出字符串。

        System.out.println("run".intern());
//run

注:字符串常量池中既可以存放字符串,也可以存放字符串的引用

常量池部分参考:
https://blog.csdn.net/qq_45737068/article/details/107149922
https://blog.csdn.net/qq_36470686/article/details/83444483

13、解释一下包装类的缓存技术?
Java 基本类型的包装类的大部分都实现了常量池技术,在常量池中缓存一些数值。
如果以字面量的形式创建的包装类对象(Integer i = 30 自动装箱)在这些数值范围内,就无需再堆中创建新的对象,直接返回常量池的引用即可。 如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。
但是,new方式创建的包装类对象,无论在不在缓存区,都会在堆中分配内存。
Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,
Character 创建了数值在 [0,127] 范围的缓存数据,
Boolean 直接返回 True Or False。
两种浮点数类型的包装类 Float,Double 并没有实现常量池技术。
包装类做运算时,会先进行自动拆箱操作?

14、直接内存?
直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。元空间利用直接内存,降低内存溢出的几率,加载更多的类。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值