1. 面试题
说一说JVM的内存结构是什么样子的,每个区域放什么,各有什么特点?(快手、搜狐)
JVM的内存结构,及各个结构的内容。(vivo)
详细介绍一下内存结构(墨迹天气)
JVM内存模型有哪些?(龙湖地产)
Java虚拟机中内存划分为哪些区域(高德地图)
JVM内存模型(中国计算机研究院、亚信)
JVM内存结构(花旗银行)
JVM 内存分哪几个区,每个区的作用是什么?(唯品会)
详解JVM内存模型(360)
JVM有那些组成,堆,栈各放了什么东西?(搜狐、万达集团)
JVM的内存模型,线程独有的放在哪里?哪些是线程共享的?哪些是线程独占的?(万达集团)
讲一下为什么JVM要分为堆、方法区等?原理是什么?(小米、搜狐)
JVM的内存模型,线程独有的放在哪里?哪些是线程共享的?哪些是线程独占的?(菜鸟)
简单说一下JVM内存结构(浪潮)
说一下JVM内存模型吧,有哪些区?分别干什么的? (百度)
JVM的内存结构划分是什么样子的? (支付宝)
JVM 内存分哪几个区,每个区的作用是什么? (蚂蚁金服)
Java虚拟机内存模型能说说吗? (蚂蚁金服)
JVM内存分布/内存结构? (蚂蚁金服)
讲讲JVM分区 (携程)
讲一下JVM内存布局 (滴滴)
Java的内存分区 (字节跳动)
讲讲JVM运行时数据库区 (字节跳动)
JVM内存模型以及分区,需要详细到每个区放什么。 (天猫)
JVM 内存分哪几个区,每个区的作用是什么? (拼多多)
JVM的内存布局以及垃圾回收原理及过程讲一下 (京东)
2. 程序计数器
JVM中的程序计数寄存器(Program Counter Register)中, Register 的命名源于CPU的寄存器,寄存器存储指令相关的现场信息。 CPU只有把数据装载到寄存器才能够运行。
- 它是一块很小的内存空间,几乎可以忽略不记。也是运行速度最快的存储区域。不会随着程序的运行需要更大的空间。
- 在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致。
- 它是唯一一个在Java 虚拟机规范中没有规定任何OutOtMemoryError 情况的区域。
使用PC寄存器存储字节码指令地址有什么用呢?(为什么使用PC寄存器记录当前线程的执行地址呢?)
因为CPU需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪开始继续执行。
JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。
PC寄存器为什么会被设定为线程私有?
我们都知道所谓的多线程在一个特定的时间段内只会执行其中某一个线程的方法,CPU会不停地做任务切换,这样必然导致经常中断或恢复,如何保证分毫无差呢?为了能够准确地记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每一个线程都分配一个PC寄存器,这样一来各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况。
3. 虚拟机栈
堆和栈的区别、谁的性能更高(艾绒软件)
为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗? (阿里)
Java 中堆和栈有什么区别?(国美)
栈和堆的区别?(蚂蚁金服)
角度一:GC;OOM
栈不存在GC垃圾回收机制但是栈也存在栈溢出(内存溢出)的情况,,而堆会有GC进行垃圾回收
角度二:栈、堆执行效率栈的执行效率仅次于程序计数器,比堆快
角度三:内存大小;数据结构栈在jdk5之前默认内存大小为256kb,jdk5开始默认内存大小为1024kb(512-1024kb之间),具体因操作系统不同而不同,而堆空间远大于栈,栈一般存基本数据类型和引用地址,而堆可存各种数据结构(如:树、哈希、链表、自定义类型等等)
角度四:栈管运行;堆管存储。栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
什么情况下会发生栈内存溢出(360)
栈存在内存溢出吗 (京东)
说一下什么情况发生栈溢出 (滴滴)
一:局部数组过大
二:递归调用层数太深
3.1 概述
Java虚拟机栈是什么?
Java虚拟机栈(Java Virtual Machine Stack),早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的Java方法调用。
是线程私有的
生命周期
生命周期和线程一致。
特点
栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器。
3.2 方法和栈帧
每个线程都有自己的栈,栈中的数据都是以栈帧(Stack Frame)的格式存在。
栈是如何运行的(OPPO)
JVM有哪些组成,堆,栈各放了什么东西。(新浪)
怎么理解栈、堆?堆中存什么?栈中存什么? (阿里)
在这个线程上正在执行的每个方法都各自对应一个栈帧(Stack Frame)。
栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息。在一条活动线程中,一个时间点上,只会有一个活动的栈帧。即只有当前正在执行的方法的栈帧(栈顶栈帧)是有效的,这个栈帧被称为当前栈帧(Current Frame),与当前栈帧相对应的方法就是当前方法(Current Method),定义这个方法的类就是当前类(Current Class)。
如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,放在栈的顶端,成为新的当前帧。
执行引擎运行的所有字节码指令只针对当前栈帧进行操作。
3.3 栈帧的内部结构
每个栈帧中存储着:
- 局部变量表(Local Variables)
- 操作数栈(Operand Stack)(或表达式栈)
- 动态链接(Dynamic Linking) (或指向运行时常量池的方法引用)
- 方法返回地址(Return Address)(或方法正常退出或者异常退出的定义)
- 一些附加信息
3.3.1 局部变量表
- 局部变量表也被称之为局部变量数组或本地变量表
- 定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型(8种)、对象引用(reference),以及returnAddress类型。
- 局部变量表所需的容量大小是在编译期确定下来的,并保存在方法的Code属性的maximum local variables数据项中。在方法运行期间是不会改变局部变量表的大小的。
存在线程安全问题么?
由于局部变量表是建立在线程的栈上,是线程的私有数据,因此不存在数据线程安全问题
slot槽位
栈帧中的局部变量表中的槽位是可以重用的,如果一个局部变量过了其作用域,那么在其作用域之后申明的新的局部变量就很有可能会复用过期局部变量的槽位,从而达到节省资源的目的。
3.3.2 操作数栈
- 栈帧中的局部变量表中的槽位是可以重用的,如果一个局部变量过了其作用域,那么在其作用域之后申明的新的局部变量就很有可能会复用过期局部变量的槽位,从而达到节省资源的目的。
- 操作数栈就是JVM执行引擎的一个工作区,当一个方法刚开始执行的时候,一个新的栈帧也会随之被创建出来,这个方法的操作数栈是空的。
- 每一个操作数栈都会拥有一个明确的栈深度用于存储数值,其所需的最大深度在编译期就定义好了,保存在方法的Code属性中,为max_stack的值。
- 栈中的任何一个元素都是可以任意的Java数据类型。
- 32bit的类型占用一个栈单位深度
- 64bit的类型占用两个栈单位深度
- 操作数栈,在方法执行过程中,根据字节码指令,并非采用访问索引的方式来进行数据访问的,而是只能通过标准的入栈(push)和出栈(pop)操作,往栈中写入数据或提取数据来完成一次数据访问。
- 如果被调用的方法带有返回值的话,其返回值将会被压入当前栈帧的操作数栈中,并更新PC寄存器中下一条需要执行的字节码指令。
- 操作数栈,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。
public void testAddOperation() {byte i = 15;int j = 8;int k = i + j;
}
字节码指令信息
public void testAddOperation();Code:0: bipush 152: istore_13: bipush 85: istore_26: iload_17: iload_28: iadd9: istore_310: return
3.3.3 动态链接
动态链接(或指向运行时常量池的方法引用)
- 每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking)。比如:invokedynamic指令
- 在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Reference)保存在class文件的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。
3.3.4 方法返回地址
- 存放调用该方法的pc寄存器的值。
- 一个方法的结束,有两种方式:
- 正常执行完成
- 出现未处理的异常,非正常退出
3.3.5 一些附加信息
栈帧中还允许携带与Java虚拟机实现相关的一些附加信息。例如,对程序调试提供支持的信息。
问题一:栈溢出的情况?
栈溢出:StackOverflowError;
举个简单的例子:在main方法中调用main方法,就会不断压栈执行,直到栈溢出;
栈的大小可以是固定大小的,也可以是动态变化(动态扩展)的。
如果是固定的,可以通过-Xss设置栈的大小;
如果是动态变化的,当栈大小到达了整个内存空间不足了,就是抛出OutOfMemory异常(java.lang.OutOfMemoryError)
问题二:调整栈大小,就能保证不出现溢出吗?
不能。因为调整栈大小,只会减少出现溢出的可能,栈大小不是可以无限扩大的,所以不能保证不出现溢出
问题三:分配的栈内存越大越好吗?
不是,因为增加栈大小,会造成每个线程的栈都变的很大,使得一定的栈空间下,能创建的线程数量会变小
问题四:垃圾回收是否会涉及到虚拟机栈?
不会;垃圾回收只会涉及到方法区和堆中,方法区和堆也会存在溢出的可能;
程序计数器,只记录运行下一行的地址,不存在溢出和垃圾回收;
虚拟机栈和本地方法栈,都是只涉及压栈和出栈,可能存在栈溢出,不存在垃圾回收。
问题五:方法中定义的局部变量是否线程安全?
局部变量是线程安全的,因为线程是私有的public class LocalVariableThreadSafe {//s1的声明方式是线程安全的,因为线程私有,在线程内创建的s1 ,不会被其它线程调用public static void method1() {//StringBuilder:线程不安全StringBuilder s1 = new StringBuilder();s1.append("a");s1.append("b");//...}//stringBuilder的操作过程:是线程不安全的,// 因为stringBuilder是外面传进来的,有可能被多个线程调用public static void method2(StringBuilder stringBuilder) {stringBuilder.append("a");stringBuilder.append("b");//...}//stringBuilder的操作:是线程不安全的;因为返回了一个stringBuilder,// stringBuilder有可能被其他线程共享public static StringBuilder method3() {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("a");stringBuilder.append("b");return stringBuilder;}//stringBuilder的操作:是线程安全的;因为返回了一个stringBuilder.toString()相当于new了一个String,// 所以stringBuilder没有被其他线程共享的可能public static String method4() {StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("a");stringBuilder.append("b");return stringBuilder.toString();/*** 结论:如果局部变量在内部产生并在内部消亡的,那就是线程安全的*/}
4 本地方法接口和本地方法栈
简单地讲,一个Native Method就是一个Java调用非Java代码的接口。
Java虚拟机栈用于管理Java方法的调用,而本地方法栈用于管理本地方法的调用。
5. 堆
5.1 概述
- 一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域。
- Java 堆区在JVM启动的时候即被创建,其空间大小也就确定了。是JVM管理的最大一块内存空间。
- 堆内存的大小是可以调节的。
- 《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。
- 堆,是GC ( Garbage Collection,垃圾收集器)执行垃圾回收的重点区域。
- 在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。
所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer, TLAB)。
5.2 堆的内部结构
Java 8及之后堆内存逻辑上分为三部分:新生区+养老区+元空间
- Young Generation Space 新生区 Young/New
又被划分为Eden区和Survivor区
- Tenure generation space 养老区 Old/Tenure
- Meta Space 元空间 Meta
Java 堆的结构是什么样子的?(猎聘)
JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor(字节跳动)
堆里面的分区:Eden,survival (from+ to),老年代,各自的特点。(京东-物流)
堆的结构?为什么两个survivor区? (蚂蚁金服)
Eden和Survior的比例分配 (蚂蚁金服)
JVM内存分区,为什么要有新生代和老年代 (小米)
JVM的内存结构,Eden和Survivor比例。 (京东)
JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor。 (京东)
JVM内存分区,为什么要有新生代和老年代? (美团)
JVM的内存结构,Eden和Survivor比例。 (京东)
- 存储在JVM中的Java对象可以被划分为两类:
- 一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速
- 另外一类对象的生命周期却非常长,在某些极端的情况下还能够与JVM的生命周期保持一致。
- Java堆区进一步细分的话,可以划分为年轻代(YoungGen)和老年代(OldGen)
- 其中年轻代又可以划分为Eden空间、Survivor0空间和Survivor1空间(有时也叫做from区、to区)。
几乎所有的Java对象都是在Eden区被new出来的。
绝大部分的Java对象的销毁都在新生代进行了。
5.3 设置堆内存大小
堆大小通过什么参数设置? (阿里)
初始堆大小和最大堆大小一样,问这样有什么好处?(亚信)
JVM中最大堆大小有没有限制? (阿里)
- Java堆区用于存储Java对象实例,那么堆的大小在JVM启动时就已经设定好了,大家可以通过选项”-Xmx”和”-Xms”来进行设置。
“-Xms”用于表示堆区的起始内存,等价于-XX:InitialHeapSize
“-Xmx”则用于表示堆区的最大内存,等价于-XX:MaxHeapSize
- 一旦堆区中的内存大小超过“-Xmx”所指定的最大内存时,将会抛出OutOfMemoryError:heap异常。
- 通常会将 -Xms 和 -Xmx两个参数配置相同的值,其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提高性能。
- heap默认最大值计算方式:如果物理内存少于192M,那么heap最大值为物理内存的一半。如果物理内存大于等于1G,那么heap的最大值为物理内存的1/4。
- heap默认最小值计算方式:最少不得少于8M,如果物理内存大于等于1G,那么默认值为物理内存的1/64,即1024/64=16M。最小堆内存在jvm启动的时候就会被初始化。
什么是空间分配担保策略?(渣打银行)
什么是空间分配担保策略?(顺丰)
什么是空间分配担保策略?(腾讯、百度)
在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,
- 如果大于,则此次Minor GC是安全的
- 如果小于,则虚拟机会查看-XX:HandlePromotionFailure设置值是否允许担保失败。
- 如果HandlePromotionFailure=true,那么会继续检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小,如果大于,则尝试进行一次Minor GC,但这次Minor GC依然是有风险的;如果小于或者HandlePromotionFailure=false,则改为进行一次Full GC。
----------------------------
在JDK 6 Update 24之后,HandlePromotionFailure参数不会再影响到虚拟机的空间分配担保策略,观察OpenJDK中的源码变化,虽然源码中还定义了HandlePromotionFailure参数,但是在代码中已经不会再使用它。JDK 6 Update 24之后的规则变为只要老年代的连续空间大于新生代对象总大小或者历次晋升的平均大小就会进行Minor GC,否则将进行Full GC。
5.4 对象分配
- 针对幸存者s0,s1区的总结:复制之后有交换,谁空谁是to.
- 关于垃圾回收:
- 频繁在新生区收集
- 很少在养老区收集
- 几乎不在永久区/元空间收集
什么时候对象会进入老年代?(渣打银行)
什么时候对象会进入老年代?(顺丰)
问什么幸存者区15次进入老年区,懂原理吗?(58)
JVM的伊甸园区,from区,to区的比例是否可调?(花旗银行)
JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代(字节跳动)
什么时候对象会进入老年代? (字节跳动)
对象在堆内存创建的生命周期 (蚂蚁金服)
重点讲讲对象如何晋升到老年代,几种主要的JVM参数 (蚂蚁金服)
新生代和老年代的内存回收策略 (蚂蚁金服)
什么时候对象可以被收回? (蚂蚁金服)
5.4.1 堆区执行流程
1.new的对象先放伊甸园区。此区有大小限制。
2.当伊甸园的空间填满时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC/YGC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区
3.然后将伊甸园中的剩余对象移动到幸存者0区。
4.如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有回收,就会放到幸存者1区。
5.如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区。
6.啥时候能去养老区呢?可以设置次数。默认是15次。
可以设置参数:-XX:MaxTenuringThreshold=<N> 设置对象晋升老年代的年龄阈值。
7.在养老区,相对悠闲。当养老区内存不足时,再次触发GC:Major GC,进行养老区的内存清理。
8.若养老区执行了Major GC之后发现依然无法进行对象的保存,就会产生OOM异常
java.lang.OutOfMemoryError: Java heap space
5.4.2 内存分配策略
内存分配策略(或对象提升(promotion)规则):
如果对象在Eden 出生并经过第一次MinorGC 后仍然存活,并且能被Survivor 容纳的话,将被移动到Survivor 空间中,并将对象年龄设为1 。对象在Survivor 区中每熬过一次MinorGC , 年龄就增加1岁,当它的年龄增加到一定程度(默认为15 岁,其实每个JVM、每个GC都有所不同)时,就会被晋升到老年代中。
5.4.3 内存分配原则
针对不同年龄段的对象分配原则如下所示:
- 优先分配到Eden
- 大对象直接分配到老年代
- 尽量避免程序中出现过多的大对象
- 长期存活的对象分配到老年代
- 动态对象年龄判断
如果Survivor 区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等到 MaxTenuringThreshold 中要求的年龄。
- 空间分配担保
-XX:HandlePromotionFailure
5.5 GC触发机制
Minor GC 与 Full GC 分别在什么时候发生?(腾讯)
老年代的垃圾回收机制什么时候触发,自动触发的阈值是多少(蚂蚁金服)
新生代的垃圾回收什么时候触发(蚂蚁金服)
简述 Java 内存分配与回收策略以及 Minor GC 和Major GC(国美)
什么时候发生Full GC(58)
简述 Java 内存分配与回收策略以及 Minor GC 和Major GC (百度)
JVM垃圾回收机制,何时触发Minor GC等操作 (蚂蚁金服)
JVM的一次完整的GC流程(从ygc到fgc)是怎样的(蚂蚁金服)
描述JVM中一次full gc过程 (腾讯)
什么情况下触发垃圾回收? (阿里)
新生代的垃圾回收什么时候触发(花旗银行)
老年代的垃圾回收机制什么时候触发,自动触发的阈值是多少(花旗银行)
5.5.1 Minor GC触发机制
年轻代GC(Minor GC)触发机制:
- 当年轻代空间不足时,就会触发Minor GC。这里的年轻代满指的是Eden区满,Survivor满不会引发GC。(每次 Minor GC 会清理年轻代的内存。)
- 因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。这一定义既清晰又易于理解。
- Minor GC会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行。
5.5.2 Major GC触发机制
老年代GC(Major GC/Full GC)触发机制:
- 指发生在老年代的GC,对象从老年代消失时,我们说“Major GC”或“Full GC”发生了。
- 出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。
- 也就是在老年代空间不足时,会先尝试触发Minor GC。如果之后空间还不足,则触发Major GC
- Major GC的速度一般会比Minor GC慢10倍以上,STW的时间更长。
- 如果Major GC 后,内存还不足,就报OOM了。
5.5.3 Full GC触发机制
Full GC触发机制:
触发Full GC 执行的情况有如下五种:
(1)调用System.gc()时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、survivor space0(From Space)区向survivor space1(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存大于该对象大小
说明:full gc是开发或调优中尽量要避免的。这样暂时时间会短一些。
5.6 堆空间分代
经研究,不同对象的生命周期不同。70%-99%的对象是临时对象。
- 新生代:有Eden、两块大小相同的Survivor(又称为from/to,s0/s1)构成,to总为空。
- 老年代:存放新生代中经历多次GC仍然存活的对象。
5.7 快速分配策略TLAB
为什么有TLAB(Thread Local Allocation Buffer)?
- 堆区是线程共享区域,任何线程都可以访问到堆区中的共享数据
- 由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的
- 为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度。
所以,多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称之为快速分配策略。
什么是TLAB?
从内存模型而不是垃圾收集的角度,对Eden区域继续进行划分,JVM为每个线程分配了一个私有缓存区域,它包含在Eden空间内。
- 尽管不是所有的对象实例都能够在TLAB中成功分配内存,但JVM确实是将TLAB作为内存分配的首选。
- 一旦对象在TLAB空间分配内存失败时,JVM就会尝试着通过使用加锁机制确保数据操作的原子性,从而直接在Eden空间中分配内存。
6. 方法区
6.1 栈、堆、方法区关系
6.2 概述
- 方法区(Method Area)与Java堆一样,是各个线程共享的内存区域。
- 方法区在JVM启动的时候被创建,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的。
- 方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展。
- 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:java.lang.OutOfMemoryError: PermGen space 或者 java.lang.OutOfMemoryError: Metaspace
加载大量的第三方的jar包;Tomcat部署的工程过多(30-50个);大量动态的生成反射类
- 关闭JVM就会释放这个区域的内存。
元空间不在虚拟机设置的内存中,而是使用本地内存。
6.3 方法区是否存在GC回收?
JVM的永久代中会发生垃圾回收么?(腾讯)
JVM的永久代中会发生垃圾回收吗?(美团)
方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。
HotSpot虚拟机对常量池的回收策略是很明确的,只要常量池中的常量没有被任何地方引用,就可以被回收。
一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻。
讲一下为什么JVM要分为堆、方法区等?原理是什么?(UC、智联)
JVM的分区了解吗,内存溢出发生在哪个位置 (亚信、BOSS)
简述各个版本内存区域的变化?(猎聘)
Java8的内存分代改进 (蚂蚁金服)
JVM的内存模型,Java8做了什么修改 (天猫)
OOM的错误,StackOverFlow错误,permgen space的错误 (蚂蚁金服)