1. Full GC触发机制有如下5种情况。
- (1)调用System.gc()时,系统建议执行Full GC,但是不必然执行。
- (2)老年代空间不足。
- (3)方法区空间不足。
- (4)老年代的最大可用连续空间小于历次晋升到老年代对象的平均大小就会进行Full GC。
- (5)由Eden区、S0(From)区向S1(To)区复制时,如果对象大小大于S1区可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。
2.大对象直接分配到老年代的几种方式
- 担保机制。minor gc后,survivor区空间不能容纳全部存活对象
- 大对象直接进入老年代。比如很长的字符串,或者很大的数组等。
- 长期存活的对象进入老年代。在堆中分配内存的对象,其内存布局的对象头中(Header)包含了 GC 分代年龄标记信息。如果对象在 eden 区出生,那么它的 GC 分代年龄会初始值为 1,每熬过一次 Minor GC 而不被回收,这个值就会增加 1 岁。当它的年龄到达一定的数值时(jdk1.7 默认是 15 岁),就会晋升到老年代中。
- 动态对象年龄判定。当 Survivor 空间中相同年龄所有对象的大小总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,而不需要达到默认的分代年龄。
3.java虚拟机中堆与栈的关系
堆中存放的是对象,栈帧中保存的是对象引用,这个引用指向对象在堆中的位置。
4.栈顶缓存(Top-of-Stack Cashing,ToS)技术
HotSpot虚拟机的设计者提出了栈顶缓存(Top-of-Stack Cashing,ToS)技术。所谓栈顶缓存技术就是当一个栈的栈顶或栈顶附近元素被频繁访问,就会将栈顶或栈顶附近的元素缓存到物理CPU的寄存器中,将原本应该在内存中的读、写操作分别变成了寄存器中的读、写操作,从而降低对内存的读、写次数,提升执行引擎的执行效率。
5.栈帧的内部结构
- 局部变量表(Local Variables)。
局部变量表最基本的存储单元是slot(变量槽)。局部变量表中存放编译期可知的各种基本数据类型(8 种)、引用(reference)类型、return Address类型的变量。
- 操作数栈(Operand Stack)(或表达式栈)。
- 动态链接(Dynamic Linking)(或指向运行时常量池的方法引用)。
- 方法返回地址(Return Address)(或方法正常退出或异常退出的定义)。
- 一些附加信息。例如,对程序调试提供支持的信息。
6、为对象分配内存:线程本地分配缓存区 TLAB(Thread Local Allocation Buffer,TLAB)
TLAB表示JVM为每个线程分配了一个私有缓存区域,这块缓存区域包含在Eden区内。简单说TLAB就是在堆内存中的Eden区分配了一块线程私有的内存区域。
(1)从内存模型角度来看,新生代区域继续对Eden区域进行划分,JVM为每个线程分配了一个私有缓存区域。
(2)多线程同时分配内存时,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分配的吞吐量,因此我们可以将这种内存分配方式称为快速分配策略。
(3)所有Open JDK衍生出来的JVM都提供了TLAB的设计。
为什么有TLAB呢?原因如下。
(1)堆区是线程共享区域,任何线程都可以访问到堆区中的共享数据。
(2)由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的。
(3)为避免多个线程操作同一地址,需要使用加锁等机制,进而影响分配速度。
7 使用“+”或String类的concat()方法拼接字符串是否存在字符串常量池中
(1)字符串常量池中不会存在相同内容的字符串常量。
(2)字面常量字符串与字面常量字符串的“+”拼接结果仍然在字符串常量池。
(3)字符串“+”拼接中只要其中有一个是变量或非字面常量,结果不会放在字符串常量池中。
(4)凡是使用concat()方法拼接的结果也不会放在字符串常量池中。
(5)如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。