4.3 栈帧内存分配越大越好吗?
????????未必,默认的栈内存通常为1024k 。栈内存不变下,栈帧过大会导致线程数变少。
4.4 方法内的局部变量是否线程安全?
- 如果方法内局部变量没有逃离方法的作用范围,它是线程安全的。
- 如果局部变量逃离了方法的作用范围(如出现在形参或返回值),需要考虑线程安全。
4.5 什么情况下会导致栈内存溢出?
- 栈帧过多导致栈内存溢出,如:递归调用
- 栈帧过大导致栈内存溢出。
4.6 堆栈的区别是什么?
- 栈内存一般会用来存储局部变量和方法调用,但堆内存是用来存储Java对象和数组的。堆有GC垃圾回收,而栈没有。
- 栈内存是线程私有的,而堆内存是线程共享的。
- 两者异常错误不同,栈内存或者堆内存不足都会抛出异常:? ?
- 栈空间不足:java.lang.StackOverFlowError。 ? ? ?
- 堆空间不足:java.lang.OutOfMemoryError。
5、介绍一下方法区(???)
5.1 方法区概述
- 方法区(Method Area)是各个线程共享的内存区域。
- 主要存储类的信息、运行时常量池。
- 虚拟机启动的时候创建,关闭虚拟机时释放。
- 如果方法区域中的内存无法满足分配请求,则会抛出OutOfMemoryError: Metaspace。
????????永久代是 JDK 1.8 之前的方法区实现,JDK 1.8 及以后方法区的实现变成了元空间。永久代在堆中,而元空间在本地内存,发生OOM的概率比永久代小。
5.2?运行时常量池
- 常量池:可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
- 当类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址。
6、直接内存(???)
? ? ? ? 直接内存不受 JVM
内存回收管理,属于操作系统的内存,常见于
NIO 操作时,用于数据缓冲区,NIO读写性能比IO高。两者区别见下图:
????????传统阻塞IO
的数据传输要走两次缓存区,而NIO只需走一次。
类加载器:
7、什么是类加载器,类加载器有哪些?(???)
????????类加载器(ClassLoader
)的主要作用就是将类的
字节码文件加载到JVM
中
,从而让
Java
程序能够启动起来。
类加载器根据各自加载范围的不同,划分为四种类加载器:
- 启动类加载器(BootStrap ClassLoader):
-
扩展类加载器(ExtClassLoader):
-
应用类加载器(AppClassLoader):
-
自定义类加载器:
8、双亲委派模型(????)
????????类加载器的体系并不是“继承”体系,而是委派体系,类加载器首先会到自己的parent中查找类或者资源,如果找不到才会到自己本地查找。类加载器的委托行为动机是为了避免相同的类被加载多次。
为什么采用双亲委派机制?
9、类装载的执行过程(???)
????????类从加载到虚拟机中开始,直到卸载为止,它的整个生命周期包括了7
个阶段:
- 加载:查找和导入class文件。
- 验证:验证类是否符合JVM规范,安全性检查。
- 准备:为类变量分配内存并设置类变量初始值。
- 解析:把类中的符号引用转换为直接引用。
- 初始化:对类的静态变量,静态代码块执行初始化操作。
- 使用:JVM 开始从入口方法开始执行用户的程序代码。
- 卸载:当用户程序代码执行完毕后,JVM便开始销毁创建的Class对象。
垃圾回收:
10、对象什么时候可以被垃圾器回收 ?(????)
????????如果没有任何的引用指向该对象了,那么这个对象现在就是垃圾,如果定位了垃圾,则有可能会被垃圾回收器回收。
定位垃圾的方式:
- 引用计数法:给对象中添加一个引用计数器,每被引用一次计数器就+1,反之-1。引用数为0时代表该对象可被回收。(缺点:循环引用时会出现内存泄漏)
- 可达性分析算法:通过一系列的称为 “GC Roots”(肯定不能当做垃圾回收的对象)?的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
- 可以作为GC Root的对象:
- 虚拟机栈(栈帧中的局部变量表)中引用的对象
- 本地方法栈(Native 方法)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 所有被同步锁持有的对象
- JNI(Java Native Interface)引用的对象
11、JVM 垃圾回收算法(????)
- 标记清除算法:先根据可达性分析算法得出的垃圾进行标记,再直接对这些标记为可回收的内容进行垃圾回收。
- 缺点:通过标记清除算法清理出来的内存,碎片化较为严重。
- 标记整理算法:标记阶段和上述类似,在清理阶段,并不是简单的直接清理可回收对象,而是将存活对象都向内存另一端移动,然后清理边界以外的垃圾,从而解决了碎片化的问题。
- 复制算法:将原有的内存空间一分为二,每次只用其中的一块,正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾的回收。无碎片、效率较高。
????????根据对象存活周期的不同将内存分为几块。一般将 Java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
????????在新生代中,每次收集都会有大量对象死去,所以可以选择”标记-复制“算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
对象回收分代回收的过程:
- 新创建的对象首先会分配到伊甸园。
- 当伊甸园内存不足,标记伊甸园和幸存区from中的存活对象。
- 使用复制算法将存活对象复制到to中,然后释放from和eden的内存。
- 一段时间后eden内存又不足,标记eden和to中存活的对象,复制到from中。
- 当幸存区对象熬过几次回收(15次),晋身至老年代中。(幸存区内存不足或大对象会提前晋身)
MinorGC、 Mixed GC 、 FullGC的区别是什么?
STW
(
Stop-The-World
):暂停所有应用程序线程,等待垃圾回收的完成 。
13、JVM 垃圾回收器(????)
?
???????停顿时间越短就越适合需要与用户交互或需要保证服务响应质量的程序,良好的响应速度能提升用户体验;而高吞吐量则可以最高效率地利用处理器资源,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的分析任务。