前言
关于JVM系列面试知识点总结了一个思维导图,分享给大家
最新2020整理收集的一些面试题(都整理成文档),有很多干货,包含netty,spring,线程,spring cloud等详细讲解,也有详细的学习规划图,面试题整理等,我感觉在面试这块讲的非常清楚:获取面试资料只需:点击这里领取!!! 暗号:CSDN
内存结构
线程共享区域
堆(用于存放对象实例)
1、新生代
(1)Eden区
(2)Survivor(from)区(设置Survivor是为了减少送到老年代的对象 )
(3)Survivor(to)区 (设置两个Survivor区是为了解决碎片化的问题)
(4)eden:survivor:survivor = 8:1:1
2、老年代
老年代:新生代=2:1
方法区
1、运行时常量池
(1)Class 文件中的常量池(编译器生成的各种字面量和符号引用)会在类加载后被放入这个区域。
(2)存储信息
符号引用
1)符号引用包含的常量
类符号引用
方法符号引用
字段符号引用
2)概念解释
一个java类(假设为People类)被编译成一个class文件时,如果People类引用了Tool类,但是在编译时People类并不知道引用类的实际内存地址,因此只能使用符号引用来代替。而在类装载器装载People类时,此时可以通过虚拟机获取Tool类的实际内存地址,因此便可以既将符号org.simple.Tool替换为Tool类的实际内存地址,及直接引用地址。 即在编译时用符号引用来代替引用类,在加载时再通过虚拟机获取该引用类的实际地址。以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用与虚拟机实现的内存布局是无关的,引用的目标不一定已经加载到内存中。
字面量
文本字符串(String a = “abc”,这个abc就是字面量)
八种基本类型(int a = 1; 这个1就是字面量)
声明为final的常量
2、静态变量
3、final类型常量
4、类信息
类的完整有效名
返回值类型
修饰符(public,private…)
变量名
方法名
方法代码
这个类型直接父类的完整有效名(除非这个类型是interface或是 java.lang.Object,两种情况下都没有父类)
类的直接接口的一个有序列表
线程私有区域
虚拟机栈
1、栈帧
(1)动态链接
符号引用和直接引用在运行时进行解析和链接的过程,叫动态链接。
前提是每一个栈帧内部都要包含一个指向运行时常量池的引用,来支持动态链接的实现。
(2)操作数栈
保存着Java 虚拟机执行过程中的数据
(3)局部变量表
1)局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
2)存放的信息
基本数据类型
对象引用
returnAddress类型
(4)方法返回地址
1)方法被调用的位置
2)方法退出的过程实际上就等同于把当前栈帧出栈
3)方法退出可能包含的操作
恢复上层方法的局部变量表和操作数栈
把返回值(如果有的话)压入调用者栈帧的操作数栈中
调整PC计数器的值以指向方法调用指令后面的一条指令
2、异常
线程请求的栈深度大于虚拟机所允许的深度(StackOverflowError )
JVM动态扩展时无法申请到足够的内存时(OutOfMemoryError )
在编译程序代码的时候,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写入到方法表的Code属性之中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体的虚拟机实现。
本地方法栈
和虚拟机栈类似,区别是本地方法栈为使用到的Native方法服务
程序计数器
1、如果线程正在执行的是一个Java方法,则指明当前线程执行的代字节码行数
2、此内存区域是唯一一个不会出现OutOfMemoryError情况的区域。
3、如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)
上述三个区域的生命周期和线程相同
直接内存
1、使用Native函数库直接分配堆外内存
2、并不是JVM运行时数据区域的一部分,但是会被频繁使用
3、避免了在Java 堆和Native 堆中来回复制数据,能够提高效率
内存分配
对象优先在Eden区分配
大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间分配时,虚拟机将发起一次Minor GC。
大对象直接进入老年代
最典型的大对象是那种很长的字符串以及数组。
长期存活对象进入老年区
如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将对象年龄设为1,对象在Survivor区中每熬过一次 Minor GC,年龄就增加1,当它的年龄增加到一定程度(默认为15)_时,就会被晋升到老年代中。
对象年龄动态判定
如果在 Survivor空间中相同年龄所有对象大小的综合大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代
空间分配担保
在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的,如果担保失败则会进行一次Full GC;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。
内存回收
Minor GC
特点
发生在新生代上,发生的较频繁,执行速度较快
触发条件
Eden区空间不足
空间分配担保
Full GC
特点
发生在老年代上,较少发生,执行速度较慢
触发条件
调用 System.gc()
老年代区域空间不足
空间分配担保失败
JDK 1.7 及以前的永久代(方法区)空间不足
CMS GC处理浮动垃圾时,如果新生代空间不足,则采用空间分配担保机制,如果老年代空间不足,则触发Full GC
内存溢出
程序在申请内存时,没有足够的内存空间
内存溢出的构造方式
堆溢出
OutOfMemoryError:不断创建对象
栈溢出
StackOverflowError: 增大本地变量表,例如不合理的递归
OutOfMemoryError:不断建立线程
方法区和运行时常量池溢出
OutOfMemoryError:通过String.intern()方法不断向常量池中添加常量,例如String.valueOf(i++).intern()
本机内存直接溢出
内存泄漏
程序在申请内存后,无法释放已申请的内存空间
原因
长生命周期的对象持有短生命周期对象的引用
例如将ArrayList设置为静态变量,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏
连接未关闭
如数据库连接、网络连接和IO连接等,只有连接被关闭后,垃圾回收器才会回收对应的对象。
变量作用域不合理
例如,1.一个变量的定义的作用范围大于其使用范围,2.如果没有及时地把对象设置为null
内部类持有外部类
Java的非静态内部类的这种创建方式,会隐式地持有外部类的引用,而且默认情况下这个引用是强引用,因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏
解决方法
将内部类定义为static
用static的变量引用匿名内部类的实例
或将匿名内部类的实例化操作放到外部类的静态方法中
Hash值改变
在集合中,如果修改了对象中的那些参与计算哈希值的字段,会导致无法从集合中单独删除当前对象,造成内存泄露
常见面试题
1、java中会存在内存泄漏吗,请简单描述。
会。自己实现堆载的数据结构时有可能会出现内存泄露。
2、64 位 JVM 中,int 的长度是多数?
Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在 32 位 和 64 位 的 Java 虚拟机中,int 类型的长度是相同的。
3、Serial 与 Parallel GC 之间的不同之处?
Serial 与 Parallel 在 GC 执行的时候都会引起 stop-the-world。它们之间主要不同 serial 收集器是默认的复制收集器,执行 GC 的时候只有一个线程,而parallel 收集器使用多个 GC 线程来执行。
4、32 位和 64 位的 JVM,int 类型变量的长度是多数?
32 位和 64 位的 JVM 中,int 类型变量的长度是相同的,都是 32 位或者 4个字节。
5、Java 中 WeakReference 与 SoftReference 的区别?
虽然 WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率,但是 WeakReference ,一旦失去最后一个强引用,就会被 GC回收,而软引用虽然不能阻止被回收,但是可以延迟到 JVM 内存不足的时候。
6、JVM 选项 -XX:+UseCompressedOops 有什么作用?为什么要使用
当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍。这也会对 CPU缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到 64 位的 JVM主要动机在于可以指定最大堆大小,通过压缩OOP 可以节省一定的内存。通过-XX:+UseCompressedOops 选项,JVM 会使用 32 位的 OOP,而不是 64 位的 OOP。
7、怎样通过 Java 程序来判断 JVM 是 32 位 还是 64位?
你可以检查某些系统属性如 sun.arch.data.model 或 os.arch 来获取该信息。
8